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     */
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       * 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    }