001/*
002 * Copyright 2008-2015 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2015 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   * 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  isRequired        Indicates whether this argument is required to
081   *                           be provided.
082   * @param  maxOccurrences    The maximum number of times this argument may be
083   *                           provided on the command line.  A value less than
084   *                           or equal to zero indicates that it may be present
085   *                           any number of times.
086   * @param  valuePlaceholder  A placeholder to display in usage information to
087   *                           indicate that a value must be provided.  It must
088   *                           not be {@code null}.
089   * @param  description       A human-readable description for this argument.
090   *                           It must not be {@code null}.
091   *
092   * @throws  ArgumentException  If there is a problem with the definition of
093   *                             this argument.
094   */
095  public FilterArgument(final Character shortIdentifier,
096                        final String longIdentifier, final boolean isRequired,
097                        final int maxOccurrences, final String valuePlaceholder,
098                        final String description)
099         throws ArgumentException
100  {
101    this(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
102         valuePlaceholder, description, (List<Filter>) null);
103  }
104
105
106
107  /**
108   * Creates a new filter argument with the provided information.
109   *
110   * @param  shortIdentifier   The short identifier for this argument.  It may
111   *                           not be {@code null} if the long identifier is
112   *                           {@code null}.
113   * @param  longIdentifier    The long identifier for this argument.  It may
114   *                           not be {@code null} if the short identifier is
115   *                           {@code null}.
116   * @param  isRequired        Indicates whether this argument is required to
117   *                           be provided.
118   * @param  maxOccurrences    The maximum number of times this argument may be
119   *                           provided on the command line.  A value less than
120   *                           or equal to zero indicates that it may be present
121   *                           any number of times.
122   * @param  valuePlaceholder  A placeholder to display in usage information to
123   *                           indicate that a value must be provided.  It must
124   *                           not be {@code null}.
125   * @param  description       A human-readable description for this argument.
126   *                           It must not be {@code null}.
127   * @param  defaultValue      The default value to use for this argument if no
128   *                           values were provided.  It may be {@code null} if
129   *                           there should be no default values.
130   *
131   * @throws  ArgumentException  If there is a problem with the definition of
132   *                             this argument.
133   */
134  public FilterArgument(final Character shortIdentifier,
135                        final String longIdentifier, final boolean isRequired,
136                        final int maxOccurrences, final String valuePlaceholder,
137                        final String description,
138                        final Filter defaultValue)
139         throws ArgumentException
140  {
141    this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
142         valuePlaceholder, description,
143         ((defaultValue == null) ? null : Arrays.asList(defaultValue)));
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 must
165   *                           not be {@code null}.
166   * @param  description       A human-readable description for this argument.
167   *                           It must not be {@code null}.
168   * @param  defaultValues     The set of default values to use for this
169   *                           argument if no values were provided.
170   *
171   * @throws  ArgumentException  If there is a problem with the definition of
172   *                             this argument.
173   */
174  public FilterArgument(final Character shortIdentifier,
175                        final String longIdentifier, final boolean isRequired,
176                        final int maxOccurrences, final String valuePlaceholder,
177                        final String description,
178                        final List<Filter> defaultValues)
179         throws ArgumentException
180  {
181    super(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
182          valuePlaceholder, description);
183
184    if (valuePlaceholder == null)
185    {
186      throw new ArgumentException(ERR_ARG_MUST_TAKE_VALUE.get(
187                                       getIdentifierString()));
188    }
189
190    if ((defaultValues == null) || defaultValues.isEmpty())
191    {
192      this.defaultValues = null;
193    }
194    else
195    {
196      this.defaultValues = Collections.unmodifiableList(defaultValues);
197    }
198
199    values = new ArrayList<Filter>(5);
200    validators = new ArrayList<ArgumentValueValidator>(5);
201  }
202
203
204
205  /**
206   * Creates a new filter argument that is a "clean" copy of the provided source
207   * argument.
208   *
209   * @param  source  The source argument to use for this argument.
210   */
211  private FilterArgument(final FilterArgument source)
212  {
213    super(source);
214
215    defaultValues = source.defaultValues;
216    validators    = new ArrayList<ArgumentValueValidator>(source.validators);
217    values        = new ArrayList<Filter>(5);
218  }
219
220
221
222  /**
223   * Retrieves the list of default values for this argument, which will be used
224   * if no values were provided.
225   *
226   * @return   The list of default values for this argument, or {@code null} if
227   *           there are no default values.
228   */
229  public List<Filter> getDefaultValues()
230  {
231    return defaultValues;
232  }
233
234
235
236  /**
237   * Updates this argument to ensure that the provided validator will be invoked
238   * for any values provided to this argument.  This validator will be invoked
239   * after all other validation has been performed for this argument.
240   *
241   * @param  validator  The argument value validator to be invoked.  It must not
242   *                    be {@code null}.
243   */
244  public void addValueValidator(final ArgumentValueValidator validator)
245  {
246    validators.add(validator);
247  }
248
249
250
251  /**
252   * {@inheritDoc}
253   */
254  @Override()
255  protected void addValue(final String valueString)
256            throws ArgumentException
257  {
258    final Filter filter;
259    try
260    {
261      filter = Filter.create(valueString);
262    }
263    catch (LDAPException le)
264    {
265      debugException(le);
266      throw new ArgumentException(ERR_FILTER_VALUE_NOT_FILTER.get(valueString,
267                                       getIdentifierString(), le.getMessage()),
268                                  le);
269    }
270
271    if (values.size() >= getMaxOccurrences())
272    {
273      throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
274                                       getIdentifierString()));
275    }
276
277    for (final ArgumentValueValidator v : validators)
278    {
279      v.validateArgumentValue(this, valueString);
280    }
281
282    values.add(filter);
283  }
284
285
286
287  /**
288   * Retrieves the value for this argument, or the default value if none was
289   * provided.  If there are multiple values, then the first will be returned.
290   *
291   * @return  The value for this argument, or the default value if none was
292   *          provided, or {@code null} if there is no value and no default
293   *          value.
294   */
295  public Filter getValue()
296  {
297    if (values.isEmpty())
298    {
299      if ((defaultValues == null) || defaultValues.isEmpty())
300      {
301        return null;
302      }
303      else
304      {
305        return defaultValues.get(0);
306      }
307    }
308    else
309    {
310      return values.get(0);
311    }
312  }
313
314
315
316  /**
317   * Retrieves the set of values for this argument, or the default values if
318   * none were provided.
319   *
320   * @return  The set of values for this argument, or the default values if none
321   *          were provided.
322   */
323  public List<Filter> getValues()
324  {
325    if (values.isEmpty() && (defaultValues != null))
326    {
327      return defaultValues;
328    }
329
330    return Collections.unmodifiableList(values);
331  }
332
333
334
335  /**
336   * {@inheritDoc}
337   */
338  @Override()
339  protected boolean hasDefaultValue()
340  {
341    return ((defaultValues != null) && (! defaultValues.isEmpty()));
342  }
343
344
345
346  /**
347   * {@inheritDoc}
348   */
349  @Override()
350  public String getDataTypeName()
351  {
352    return INFO_FILTER_TYPE_NAME.get();
353  }
354
355
356
357  /**
358   * {@inheritDoc}
359   */
360  @Override()
361  public String getValueConstraints()
362  {
363    return INFO_FILTER_CONSTRAINTS.get();
364  }
365
366
367
368  /**
369   * {@inheritDoc}
370   */
371  @Override()
372  public FilterArgument getCleanCopy()
373  {
374    return new FilterArgument(this);
375  }
376
377
378
379  /**
380   * {@inheritDoc}
381   */
382  @Override()
383  public void toString(final StringBuilder buffer)
384  {
385    buffer.append("FilterArgument(");
386    appendBasicToStringInfo(buffer);
387
388    if ((defaultValues != null) && (! defaultValues.isEmpty()))
389    {
390      if (defaultValues.size() == 1)
391      {
392        buffer.append(", defaultValue='");
393        buffer.append(defaultValues.get(0).toString());
394      }
395      else
396      {
397        buffer.append(", defaultValues={");
398
399        final Iterator<Filter> iterator = defaultValues.iterator();
400        while (iterator.hasNext())
401        {
402          buffer.append('\'');
403          buffer.append(iterator.next().toString());
404          buffer.append('\'');
405
406          if (iterator.hasNext())
407          {
408            buffer.append(", ");
409          }
410        }
411
412        buffer.append('}');
413      }
414    }
415
416    buffer.append(')');
417  }
418}