001/*
002 * Copyright 2008-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 UnboundID Corp.
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.Arrays;
027import java.util.Collections;
028import java.util.Iterator;
029import java.util.List;
030
031import com.unboundid.ldap.sdk.Filter;
032import com.unboundid.ldap.sdk.LDAPException;
033import com.unboundid.util.Mutable;
034import com.unboundid.util.ThreadSafety;
035import com.unboundid.util.ThreadSafetyLevel;
036
037import static com.unboundid.util.Debug.*;
038import static com.unboundid.util.args.ArgsMessages.*;
039
040
041
042/**
043 * This class defines an argument that is intended to hold one or more
044 * search filter values.  Filter arguments must take values, and those values
045 * must be able to be parsed as LDAP search filters.
046 */
047@Mutable()
048@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
049public final class FilterArgument
050       extends Argument
051{
052  /**
053   * The serial version UID for this serializable class.
054   */
055  private static final long serialVersionUID = -1889200072476038957L;
056
057
058
059  // The set of values assigned to this argument.
060  private final ArrayList<Filter> values;
061
062  // The argument value validators that have been registered for this argument.
063  private final List<ArgumentValueValidator> validators;
064
065  // The list of default values for this argument.
066  private final List<Filter> defaultValues;
067
068
069
070  /**
071   * Creates a new filter argument with the provided information.  It will not
072   * be required, will permit at most one occurrence, will use a default
073   * placeholder, and will not have a default value.
074   *
075   * @param  shortIdentifier   The short identifier for this argument.  It may
076   *                           not be {@code null} if the long identifier is
077   *                           {@code null}.
078   * @param  longIdentifier    The long identifier for this argument.  It may
079   *                           not be {@code null} if the short identifier is
080   *                           {@code null}.
081   * @param  description       A human-readable description for this argument.
082   *                           It must not be {@code null}.
083   *
084   * @throws  ArgumentException  If there is a problem with the definition of
085   *                             this argument.
086   */
087  public FilterArgument(final Character shortIdentifier,
088                        final String longIdentifier, final String description)
089         throws ArgumentException
090  {
091    this(shortIdentifier, longIdentifier, false, 1, null, description);
092  }
093
094
095
096  /**
097   * Creates a new filter argument with the provided information.  It will not
098   * have a default value.
099   *
100   * @param  shortIdentifier   The short identifier for this argument.  It may
101   *                           not be {@code null} if the long identifier is
102   *                           {@code null}.
103   * @param  longIdentifier    The long identifier for this argument.  It may
104   *                           not be {@code null} if the short identifier is
105   *                           {@code null}.
106   * @param  isRequired        Indicates whether this argument is required to
107   *                           be provided.
108   * @param  maxOccurrences    The maximum number of times this argument may be
109   *                           provided on the command line.  A value less than
110   *                           or equal to zero indicates that it may be present
111   *                           any number of times.
112   * @param  valuePlaceholder  A placeholder to display in usage information to
113   *                           indicate that a value must be provided.  It may
114   *                           be {@code null} if a default placeholder should
115   *                           be used.
116   * @param  description       A human-readable description for this argument.
117   *                           It must not be {@code null}.
118   *
119   * @throws  ArgumentException  If there is a problem with the definition of
120   *                             this argument.
121   */
122  public FilterArgument(final Character shortIdentifier,
123                        final String longIdentifier, final boolean isRequired,
124                        final int maxOccurrences, final String valuePlaceholder,
125                        final String description)
126         throws ArgumentException
127  {
128    this(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
129         valuePlaceholder, description, (List<Filter>) null);
130  }
131
132
133
134  /**
135   * Creates a new filter argument with the provided information.
136   *
137   * @param  shortIdentifier   The short identifier for this argument.  It may
138   *                           not be {@code null} if the long identifier is
139   *                           {@code null}.
140   * @param  longIdentifier    The long identifier for this argument.  It may
141   *                           not be {@code null} if the short identifier is
142   *                           {@code null}.
143   * @param  isRequired        Indicates whether this argument is required to
144   *                           be provided.
145   * @param  maxOccurrences    The maximum number of times this argument may be
146   *                           provided on the command line.  A value less than
147   *                           or equal to zero indicates that it may be present
148   *                           any number of times.
149   * @param  valuePlaceholder  A placeholder to display in usage information to
150   *                           indicate that a value must be provided.  It may
151   *                           be {@code null} if a default placeholder should
152   *                           be used.
153   * @param  description       A human-readable description for this argument.
154   *                           It must not be {@code null}.
155   * @param  defaultValue      The default value to use for this argument if no
156   *                           values were provided.  It may be {@code null} if
157   *                           there should be no default values.
158   *
159   * @throws  ArgumentException  If there is a problem with the definition of
160   *                             this argument.
161   */
162  public FilterArgument(final Character shortIdentifier,
163                        final String longIdentifier, final boolean isRequired,
164                        final int maxOccurrences, final String valuePlaceholder,
165                        final String description,
166                        final Filter defaultValue)
167         throws ArgumentException
168  {
169    this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
170         valuePlaceholder, description,
171         ((defaultValue == null) ? null : Arrays.asList(defaultValue)));
172  }
173
174
175
176  /**
177   * Creates a new filter argument with the provided information.
178   *
179   * @param  shortIdentifier   The short identifier for this argument.  It may
180   *                           not be {@code null} if the long identifier is
181   *                           {@code null}.
182   * @param  longIdentifier    The long identifier for this argument.  It may
183   *                           not be {@code null} if the short identifier is
184   *                           {@code null}.
185   * @param  isRequired        Indicates whether this argument is required to
186   *                           be provided.
187   * @param  maxOccurrences    The maximum number of times this argument may be
188   *                           provided on the command line.  A value less than
189   *                           or equal to zero indicates that it may be present
190   *                           any number of times.
191   * @param  valuePlaceholder  A placeholder to display in usage information to
192   *                           indicate that a value must be provided.  It may
193   *                           be {@code null} if a default placeholder should
194   *                           be used.
195   * @param  description       A human-readable description for this argument.
196   *                           It must not be {@code null}.
197   * @param  defaultValues     The set of default values to use for this
198   *                           argument if no values were provided.
199   *
200   * @throws  ArgumentException  If there is a problem with the definition of
201   *                             this argument.
202   */
203  public FilterArgument(final Character shortIdentifier,
204                        final String longIdentifier, final boolean isRequired,
205                        final int maxOccurrences, final String valuePlaceholder,
206                        final String description,
207                        final List<Filter> defaultValues)
208         throws ArgumentException
209  {
210    super(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
211         (valuePlaceholder == null)
212              ? INFO_PLACEHOLDER_FILTER.get()
213              : valuePlaceholder,
214         description);
215
216    if ((defaultValues == null) || defaultValues.isEmpty())
217    {
218      this.defaultValues = null;
219    }
220    else
221    {
222      this.defaultValues = Collections.unmodifiableList(defaultValues);
223    }
224
225    values = new ArrayList<Filter>(5);
226    validators = new ArrayList<ArgumentValueValidator>(5);
227  }
228
229
230
231  /**
232   * Creates a new filter argument that is a "clean" copy of the provided source
233   * argument.
234   *
235   * @param  source  The source argument to use for this argument.
236   */
237  private FilterArgument(final FilterArgument source)
238  {
239    super(source);
240
241    defaultValues = source.defaultValues;
242    validators    = new ArrayList<ArgumentValueValidator>(source.validators);
243    values        = new ArrayList<Filter>(5);
244  }
245
246
247
248  /**
249   * Retrieves the list of default values for this argument, which will be used
250   * if no values were provided.
251   *
252   * @return   The list of default values for this argument, or {@code null} if
253   *           there are no default values.
254   */
255  public List<Filter> getDefaultValues()
256  {
257    return defaultValues;
258  }
259
260
261
262  /**
263   * Updates this argument to ensure that the provided validator will be invoked
264   * for any values provided to this argument.  This validator will be invoked
265   * after all other validation has been performed for this argument.
266   *
267   * @param  validator  The argument value validator to be invoked.  It must not
268   *                    be {@code null}.
269   */
270  public void addValueValidator(final ArgumentValueValidator validator)
271  {
272    validators.add(validator);
273  }
274
275
276
277  /**
278   * {@inheritDoc}
279   */
280  @Override()
281  protected void addValue(final String valueString)
282            throws ArgumentException
283  {
284    final Filter filter;
285    try
286    {
287      filter = Filter.create(valueString);
288    }
289    catch (LDAPException le)
290    {
291      debugException(le);
292      throw new ArgumentException(ERR_FILTER_VALUE_NOT_FILTER.get(valueString,
293                                       getIdentifierString(), le.getMessage()),
294                                  le);
295    }
296
297    if (values.size() >= getMaxOccurrences())
298    {
299      throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
300                                       getIdentifierString()));
301    }
302
303    for (final ArgumentValueValidator v : validators)
304    {
305      v.validateArgumentValue(this, valueString);
306    }
307
308    values.add(filter);
309  }
310
311
312
313  /**
314   * Retrieves the value for this argument, or the default value if none was
315   * provided.  If there are multiple values, then the first will be returned.
316   *
317   * @return  The value for this argument, or the default value if none was
318   *          provided, or {@code null} if there is no value and no default
319   *          value.
320   */
321  public Filter getValue()
322  {
323    if (values.isEmpty())
324    {
325      if ((defaultValues == null) || defaultValues.isEmpty())
326      {
327        return null;
328      }
329      else
330      {
331        return defaultValues.get(0);
332      }
333    }
334    else
335    {
336      return values.get(0);
337    }
338  }
339
340
341
342  /**
343   * Retrieves the set of values for this argument, or the default values if
344   * none were provided.
345   *
346   * @return  The set of values for this argument, or the default values if none
347   *          were provided.
348   */
349  public List<Filter> getValues()
350  {
351    if (values.isEmpty() && (defaultValues != null))
352    {
353      return defaultValues;
354    }
355
356    return Collections.unmodifiableList(values);
357  }
358
359
360
361  /**
362   * {@inheritDoc}
363   */
364  @Override()
365  public List<String> getValueStringRepresentations(final boolean useDefault)
366  {
367    final List<Filter> filters;
368    if (values.isEmpty())
369    {
370      if (useDefault)
371      {
372        filters = defaultValues;
373      }
374      else
375      {
376        return Collections.emptyList();
377      }
378    }
379    else
380    {
381      filters = values;
382    }
383
384    if ((filters == null) || filters.isEmpty())
385    {
386      return Collections.emptyList();
387    }
388
389    final ArrayList<String> valueStrings =
390         new ArrayList<String>(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}