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.Filter;
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 * search filter values.  Filter arguments must take values, and those values
059 * must be able to be parsed as LDAP search filters.
060 */
061@Mutable()
062@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
063public final class FilterArgument
064       extends Argument
065{
066  /**
067   * The serial version UID for this serializable class.
068   */
069  private static final long serialVersionUID = -1889200072476038957L;
070
071
072
073  // The set of values assigned to this argument.
074  private final ArrayList<Filter> 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<Filter> defaultValues;
081
082
083
084  /**
085   * Creates a new filter argument with the provided information.  It will not
086   * be 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 FilterArgument(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 filter argument with the provided information.  It will not
112   * have 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 FilterArgument(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<Filter>) null);
144  }
145
146
147
148  /**
149   * Creates a new filter 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.  It may be {@code null} if
171   *                           there should be no default values.
172   *
173   * @throws  ArgumentException  If there is a problem with the definition of
174   *                             this argument.
175   */
176  public FilterArgument(final Character shortIdentifier,
177                        final String longIdentifier, final boolean isRequired,
178                        final int maxOccurrences, final String valuePlaceholder,
179                        final String description,
180                        final Filter defaultValue)
181         throws ArgumentException
182  {
183    this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
184         valuePlaceholder, description,
185         ((defaultValue == null)
186              ? null
187              : Collections.singletonList(defaultValue)));
188  }
189
190
191
192  /**
193   * Creates a new filter argument with the provided information.
194   *
195   * @param  shortIdentifier   The short identifier for this argument.  It may
196   *                           not be {@code null} if the long identifier is
197   *                           {@code null}.
198   * @param  longIdentifier    The long identifier for this argument.  It may
199   *                           not be {@code null} if the short identifier is
200   *                           {@code null}.
201   * @param  isRequired        Indicates whether this argument is required to
202   *                           be provided.
203   * @param  maxOccurrences    The maximum number of times this argument may be
204   *                           provided on the command line.  A value less than
205   *                           or equal to zero indicates that it may be present
206   *                           any number of times.
207   * @param  valuePlaceholder  A placeholder to display in usage information to
208   *                           indicate that a value must be provided.  It may
209   *                           be {@code null} if a default placeholder should
210   *                           be used.
211   * @param  description       A human-readable description for this argument.
212   *                           It must not be {@code null}.
213   * @param  defaultValues     The set of default values to use for this
214   *                           argument if no values were provided.
215   *
216   * @throws  ArgumentException  If there is a problem with the definition of
217   *                             this argument.
218   */
219  public FilterArgument(final Character shortIdentifier,
220                        final String longIdentifier, final boolean isRequired,
221                        final int maxOccurrences, final String valuePlaceholder,
222                        final String description,
223                        final List<Filter> defaultValues)
224         throws ArgumentException
225  {
226    super(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
227         (valuePlaceholder == null)
228              ? INFO_PLACEHOLDER_FILTER.get()
229              : valuePlaceholder,
230         description);
231
232    if ((defaultValues == null) || defaultValues.isEmpty())
233    {
234      this.defaultValues = null;
235    }
236    else
237    {
238      this.defaultValues = Collections.unmodifiableList(defaultValues);
239    }
240
241    values = new ArrayList<>(5);
242    validators = new ArrayList<>(5);
243  }
244
245
246
247  /**
248   * Creates a new filter argument that is a "clean" copy of the provided source
249   * argument.
250   *
251   * @param  source  The source argument to use for this argument.
252   */
253  private FilterArgument(final FilterArgument source)
254  {
255    super(source);
256
257    defaultValues = source.defaultValues;
258    validators    = new ArrayList<>(source.validators);
259    values        = new ArrayList<>(5);
260  }
261
262
263
264  /**
265   * Retrieves the list of default values for this argument, which will be used
266   * if no values were provided.
267   *
268   * @return   The list of default values for this argument, or {@code null} if
269   *           there are no default values.
270   */
271  public List<Filter> getDefaultValues()
272  {
273    return defaultValues;
274  }
275
276
277
278  /**
279   * Updates this argument to ensure that the provided validator will be invoked
280   * for any values provided to this argument.  This validator will be invoked
281   * after all other validation has been performed for this argument.
282   *
283   * @param  validator  The argument value validator to be invoked.  It must not
284   *                    be {@code null}.
285   */
286  public void addValueValidator(final ArgumentValueValidator validator)
287  {
288    validators.add(validator);
289  }
290
291
292
293  /**
294   * {@inheritDoc}
295   */
296  @Override()
297  protected void addValue(final String valueString)
298            throws ArgumentException
299  {
300    final Filter filter;
301    try
302    {
303      filter = Filter.create(valueString);
304    }
305    catch (final LDAPException le)
306    {
307      Debug.debugException(le);
308      throw new ArgumentException(ERR_FILTER_VALUE_NOT_FILTER.get(valueString,
309                                       getIdentifierString(), le.getMessage()),
310                                  le);
311    }
312
313    if (values.size() >= getMaxOccurrences())
314    {
315      throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
316                                       getIdentifierString()));
317    }
318
319    for (final ArgumentValueValidator v : validators)
320    {
321      v.validateArgumentValue(this, valueString);
322    }
323
324    values.add(filter);
325  }
326
327
328
329  /**
330   * Retrieves the value for this argument, or the default value if none was
331   * provided.  If there are multiple values, then the first will be returned.
332   *
333   * @return  The value for this argument, or the default value if none was
334   *          provided, or {@code null} if there is no value and no default
335   *          value.
336   */
337  public Filter getValue()
338  {
339    if (values.isEmpty())
340    {
341      if ((defaultValues == null) || defaultValues.isEmpty())
342      {
343        return null;
344      }
345      else
346      {
347        return defaultValues.get(0);
348      }
349    }
350    else
351    {
352      return values.get(0);
353    }
354  }
355
356
357
358  /**
359   * Retrieves the set of values for this argument, or the default values if
360   * none were provided.
361   *
362   * @return  The set of values for this argument, or the default values if none
363   *          were provided.
364   */
365  public List<Filter> getValues()
366  {
367    if (values.isEmpty() && (defaultValues != null))
368    {
369      return defaultValues;
370    }
371
372    return Collections.unmodifiableList(values);
373  }
374
375
376
377  /**
378   * {@inheritDoc}
379   */
380  @Override()
381  public List<String> getValueStringRepresentations(final boolean useDefault)
382  {
383    final List<Filter> filters;
384    if (values.isEmpty())
385    {
386      if (useDefault)
387      {
388        filters = defaultValues;
389      }
390      else
391      {
392        return Collections.emptyList();
393      }
394    }
395    else
396    {
397      filters = values;
398    }
399
400    if ((filters == null) || filters.isEmpty())
401    {
402      return Collections.emptyList();
403    }
404
405    final ArrayList<String> valueStrings = new ArrayList<>(filters.size());
406    for (final Filter f : filters)
407    {
408      valueStrings.add(f.toString());
409    }
410    return Collections.unmodifiableList(valueStrings);
411  }
412
413
414
415  /**
416   * {@inheritDoc}
417   */
418  @Override()
419  protected boolean hasDefaultValue()
420  {
421    return ((defaultValues != null) && (! defaultValues.isEmpty()));
422  }
423
424
425
426  /**
427   * {@inheritDoc}
428   */
429  @Override()
430  public String getDataTypeName()
431  {
432    return INFO_FILTER_TYPE_NAME.get();
433  }
434
435
436
437  /**
438   * {@inheritDoc}
439   */
440  @Override()
441  public String getValueConstraints()
442  {
443    return INFO_FILTER_CONSTRAINTS.get();
444  }
445
446
447
448  /**
449   * {@inheritDoc}
450   */
451  @Override()
452  protected void reset()
453  {
454    super.reset();
455    values.clear();
456  }
457
458
459
460  /**
461   * {@inheritDoc}
462   */
463  @Override()
464  public FilterArgument getCleanCopy()
465  {
466    return new FilterArgument(this);
467  }
468
469
470
471  /**
472   * {@inheritDoc}
473   */
474  @Override()
475  protected void addToCommandLine(final List<String> argStrings)
476  {
477    if (values != null)
478    {
479      for (final Filter f : values)
480      {
481        argStrings.add(getIdentifierString());
482        if (isSensitive())
483        {
484          argStrings.add("***REDACTED***");
485        }
486        else
487        {
488          argStrings.add(f.toString());
489        }
490      }
491    }
492  }
493
494
495
496  /**
497   * {@inheritDoc}
498   */
499  @Override()
500  public void toString(final StringBuilder buffer)
501  {
502    buffer.append("FilterArgument(");
503    appendBasicToStringInfo(buffer);
504
505    if ((defaultValues != null) && (! defaultValues.isEmpty()))
506    {
507      if (defaultValues.size() == 1)
508      {
509        buffer.append(", defaultValue='");
510        buffer.append(defaultValues.get(0).toString());
511      }
512      else
513      {
514        buffer.append(", defaultValues={");
515
516        final Iterator<Filter> iterator = defaultValues.iterator();
517        while (iterator.hasNext())
518        {
519          buffer.append('\'');
520          buffer.append(iterator.next().toString());
521          buffer.append('\'');
522
523          if (iterator.hasNext())
524          {
525            buffer.append(", ");
526          }
527        }
528
529        buffer.append('}');
530      }
531    }
532
533    buffer.append(')');
534  }
535}