001    /*
002     * Copyright 2008-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2016 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     */
021    package com.unboundid.util.args;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.Collections;
028    import java.util.Iterator;
029    import java.util.List;
030    
031    import com.unboundid.ldap.sdk.Filter;
032    import com.unboundid.ldap.sdk.LDAPException;
033    import com.unboundid.util.Mutable;
034    import com.unboundid.util.ThreadSafety;
035    import com.unboundid.util.ThreadSafetyLevel;
036    
037    import static com.unboundid.util.Debug.*;
038    import 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)
049    public 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    }