001    /*
002     * Copyright 2010-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2010-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.Arrays;
026    import java.util.Collections;
027    import java.util.HashMap;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.concurrent.atomic.AtomicReference;
031    
032    import com.unboundid.ldap.sdk.SearchScope;
033    import com.unboundid.util.Mutable;
034    import com.unboundid.util.StaticUtils;
035    import com.unboundid.util.ThreadSafety;
036    import com.unboundid.util.ThreadSafetyLevel;
037    
038    import static com.unboundid.util.args.ArgsMessages.*;
039    
040    
041    
042    /**
043     * This class defines an argument that is intended to hold one search scope
044     * values.  Scope arguments must take values, and those arguments must represent
045     * valid search scopes.  Supported scope values include:
046     * <UL>
047     *   <LI>baseObject scope -- base, baseObject, base-object, 0</LI>
048     *   <LI>singleLevel scope -- one, singleLevel, single-level, oneLevel,
049     *       one-level, 1</LI>
050     *   <LI>wholeSubtree scope -- sub, subtree, wholeSubtree, whole-subtree, 2</LI>
051     *   <LI>subordinateSubtree scope -- subord, subordinate, subordinates,
052     *       subordinateSubtree, subordinate-subtree, 3</LI>
053     * </UL>
054     */
055    @Mutable()
056    @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
057    public final class ScopeArgument
058           extends Argument
059    {
060      /**
061       * A map of value strings to the corresponding search scopes.
062       */
063      private static final Map<String,SearchScope> SCOPE_STRINGS;
064    
065      static
066      {
067        final HashMap<String,SearchScope> scopeMap =
068             new HashMap<String,SearchScope>(21);
069    
070        scopeMap.put("base", SearchScope.BASE);
071        scopeMap.put("baseobject", SearchScope.BASE);
072        scopeMap.put("base-object", SearchScope.BASE);
073        scopeMap.put("0", SearchScope.BASE);
074    
075        scopeMap.put("one", SearchScope.ONE);
076        scopeMap.put("singlelevel", SearchScope.ONE);
077        scopeMap.put("single-level", SearchScope.ONE);
078        scopeMap.put("onelevel", SearchScope.ONE);
079        scopeMap.put("one-level", SearchScope.ONE);
080        scopeMap.put("1", SearchScope.ONE);
081    
082        scopeMap.put("sub", SearchScope.SUB);
083        scopeMap.put("subtree", SearchScope.SUB);
084        scopeMap.put("wholesubtree", SearchScope.SUB);
085        scopeMap.put("whole-subtree", SearchScope.SUB);
086        scopeMap.put("2", SearchScope.SUB);
087    
088        scopeMap.put("subord", SearchScope.SUBORDINATE_SUBTREE);
089        scopeMap.put("subordinate", SearchScope.SUBORDINATE_SUBTREE);
090        scopeMap.put("subordinates", SearchScope.SUBORDINATE_SUBTREE);
091        scopeMap.put("subordinatesubtree", SearchScope.SUBORDINATE_SUBTREE);
092        scopeMap.put("subordinate-subtree", SearchScope.SUBORDINATE_SUBTREE);
093        scopeMap.put("3", SearchScope.SUBORDINATE_SUBTREE);
094    
095        SCOPE_STRINGS = Collections.unmodifiableMap(scopeMap);
096      }
097    
098    
099    
100      /**
101       * The serial version UID for this serializable class.
102       */
103      private static final long serialVersionUID = 5962857448814911423L;
104    
105    
106    
107      // The value assigned to this argument.
108      private final AtomicReference<SearchScope> value;
109    
110      // The default value for this argument.
111      private final SearchScope defaultValue;
112    
113    
114    
115      /**
116       * Creates a new search scope argument with the provided information.  It will
117       * not be required, will use a default placeholder, and will not have a
118       * default value.
119       *
120       * @param  shortIdentifier   The short identifier for this argument.  It may
121       *                           not be {@code null} if the long identifier is
122       *                           {@code null}.
123       * @param  longIdentifier    The long identifier for this argument.  It may
124       *                           not be {@code null} if the short identifier is
125       *                           {@code null}.
126       * @param  description       A human-readable description for this argument.
127       *                           It must not be {@code null}.
128       *
129       * @throws  ArgumentException  If there is a problem with the definition of
130       *                             this argument.
131       */
132      public ScopeArgument(final Character shortIdentifier,
133                           final String longIdentifier, final String description)
134             throws ArgumentException
135      {
136        this(shortIdentifier, longIdentifier, false, null, description);
137      }
138    
139    
140    
141      /**
142       * Creates a new search scope argument with the provided information.  It will
143       * not have a default value.
144       *
145       * @param  shortIdentifier   The short identifier for this argument.  It may
146       *                           not be {@code null} if the long identifier is
147       *                           {@code null}.
148       * @param  longIdentifier    The long identifier for this argument.  It may
149       *                           not be {@code null} if the short identifier is
150       *                           {@code null}.
151       * @param  isRequired        Indicates whether this argument is required to
152       *                           be provided.
153       * @param  valuePlaceholder  A placeholder to display in usage information to
154       *                           indicate that a value must be provided.  It may
155       *                           be {@code null} if a default placeholder should
156       *                           be used.
157       * @param  description       A human-readable description for this argument.
158       *                           It must not be {@code null}.
159       *
160       * @throws  ArgumentException  If there is a problem with the definition of
161       *                             this argument.
162       */
163      public ScopeArgument(final Character shortIdentifier,
164                           final String longIdentifier, final boolean isRequired,
165                           final String valuePlaceholder, final String description)
166             throws ArgumentException
167      {
168        this(shortIdentifier, longIdentifier, isRequired,  valuePlaceholder,
169             description, null);
170      }
171    
172    
173    
174    
175    
176    
177      /**
178       * Creates a new search scope argument with the provided information.
179       *
180       * @param  shortIdentifier   The short identifier for this argument.  It may
181       *                           not be {@code null} if the long identifier is
182       *                           {@code null}.
183       * @param  longIdentifier    The long identifier for this argument.  It may
184       *                           not be {@code null} if the short identifier is
185       *                           {@code null}.
186       * @param  isRequired        Indicates whether this argument is required to
187       *                           be provided.
188       * @param  valuePlaceholder  A placeholder to display in usage information to
189       *                           indicate that a value must be provided.  It may
190       *                           be {@code null} if a default placeholder should
191       *                           be used.
192       * @param  description       A human-readable description for this argument.
193       *                           It must not be {@code null}.
194       * @param  defaultValue      The default value to use for this argument if no
195       *                           values were provided.  It may be {@code null} if
196       *                           there should be no default values.
197       *
198       * @throws  ArgumentException  If there is a problem with the definition of
199       *                             this argument.
200       */
201      public ScopeArgument(final Character shortIdentifier,
202                           final String longIdentifier, final boolean isRequired,
203                           final String valuePlaceholder, final String description,
204                           final SearchScope defaultValue)
205             throws ArgumentException
206      {
207        super(shortIdentifier, longIdentifier, isRequired,  1,
208             (valuePlaceholder == null)
209                  ? INFO_PLACEHOLDER_SCOPE.get()
210                  : valuePlaceholder,
211             description);
212    
213        this.defaultValue = defaultValue;
214    
215        value = new AtomicReference<SearchScope>();
216      }
217    
218    
219    
220      /**
221       * Creates a new scope argument that is a "clean" copy of the provided
222       * source argument.
223       *
224       * @param  source  The source argument to use for this argument.
225       */
226      private ScopeArgument(final ScopeArgument source)
227      {
228        super(source);
229    
230        defaultValue = source.defaultValue;
231        value        = new AtomicReference<SearchScope>();
232      }
233    
234    
235    
236      /**
237       * Retrieves the default value for this argument, which will be used if no
238       * value was provided.
239       *
240       * @return  The default value for this argument, or {@code null} if there is
241       *          no default value.
242       */
243      public SearchScope getDefaultValue()
244      {
245        return defaultValue;
246      }
247    
248    
249    
250      /**
251       * {@inheritDoc}
252       */
253      @Override()
254      protected void addValue(final String valueString)
255                throws ArgumentException
256      {
257        final SearchScope scope =
258             SCOPE_STRINGS.get(StaticUtils.toLowerCase(valueString));
259        if (scope == null)
260        {
261          throw new ArgumentException(ERR_SCOPE_VALUE_NOT_VALID.get(valueString,
262               getIdentifierString()));
263        }
264    
265        if (! value.compareAndSet(null, scope))
266        {
267          throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
268                                           getIdentifierString()));
269        }
270      }
271    
272    
273    
274      /**
275       * Retrieves the value for this argument, or the default value if none was
276       * provided.
277       *
278       * @return  The value for this argument, or the default value if none was
279       *          provided, or {@code null} if there is no value and no default
280       *          value.
281       */
282      public SearchScope getValue()
283      {
284        final SearchScope s = value.get();
285        if (s == null)
286        {
287          return defaultValue;
288        }
289        else
290        {
291          return s;
292        }
293      }
294    
295    
296    
297      /**
298       * {@inheritDoc}
299       */
300      @Override()
301      public List<String> getValueStringRepresentations(final boolean useDefault)
302      {
303        SearchScope s = value.get();
304        if (useDefault && (s == null))
305        {
306          s = defaultValue;
307        }
308    
309        if (s == null)
310        {
311          return Collections.emptyList();
312        }
313    
314        final String scopeStr;
315        switch (s.intValue())
316        {
317          case SearchScope.BASE_INT_VALUE:
318            scopeStr = "base";
319            break;
320          case SearchScope.ONE_INT_VALUE:
321            scopeStr = "one";
322            break;
323          case SearchScope.SUB_INT_VALUE:
324            scopeStr = "sub";
325            break;
326          case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE:
327            scopeStr = "subordinates";
328            break;
329          default:
330            scopeStr = s.getName();
331            break;
332        }
333    
334        return Collections.unmodifiableList(Arrays.asList(scopeStr));
335      }
336    
337    
338    
339      /**
340       * {@inheritDoc}
341       */
342      @Override()
343      protected boolean hasDefaultValue()
344      {
345        return (defaultValue != null);
346      }
347    
348    
349    
350      /**
351       * {@inheritDoc}
352       */
353      @Override()
354      public String getDataTypeName()
355      {
356        return INFO_SCOPE_TYPE_NAME.get();
357      }
358    
359    
360    
361      /**
362       * {@inheritDoc}
363       */
364      @Override()
365      public String getValueConstraints()
366      {
367        return INFO_SCOPE_CONSTRAINTS.get();
368      }
369    
370    
371    
372      /**
373       * {@inheritDoc}
374       */
375      @Override()
376      protected void reset()
377      {
378        super.reset();
379        value.set(null);
380      }
381    
382    
383    
384      /**
385       * {@inheritDoc}
386       */
387      @Override()
388      public ScopeArgument getCleanCopy()
389      {
390        return new ScopeArgument(this);
391      }
392    
393    
394    
395      /**
396       * {@inheritDoc}
397       */
398      @Override()
399      protected void addToCommandLine(final List<String> argStrings)
400      {
401        final SearchScope s = value.get();
402        if (s != null)
403        {
404          if (isSensitive())
405          {
406            argStrings.add(getIdentifierString());
407            argStrings.add("***REDACTED***");
408            return;
409          }
410    
411          switch (s.intValue())
412          {
413            case SearchScope.BASE_INT_VALUE:
414              argStrings.add(getIdentifierString());
415              argStrings.add("base");
416              break;
417            case SearchScope.ONE_INT_VALUE:
418              argStrings.add(getIdentifierString());
419              argStrings.add("one");
420              break;
421            case SearchScope.SUB_INT_VALUE:
422              argStrings.add(getIdentifierString());
423              argStrings.add("sub");
424              break;
425            case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE:
426              argStrings.add(getIdentifierString());
427              argStrings.add("subordinates");
428              break;
429          }
430        }
431      }
432    
433    
434    
435      /**
436       * {@inheritDoc}
437       */
438      @Override()
439      public void toString(final StringBuilder buffer)
440      {
441        buffer.append("ScopeArgument(");
442        appendBasicToStringInfo(buffer);
443    
444        if (defaultValue != null)
445        {
446          buffer.append(", defaultValue='");
447          switch (defaultValue.intValue())
448          {
449            case SearchScope.BASE_INT_VALUE:
450              buffer.append("base");
451              break;
452            case SearchScope.ONE_INT_VALUE:
453              buffer.append("one");
454              break;
455            case SearchScope.SUB_INT_VALUE:
456              buffer.append("sub");
457              break;
458            case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE:
459              buffer.append("subordinate");
460              break;
461            default:
462              buffer.append(defaultValue.intValue());
463              break;
464          }
465          buffer.append('\'');
466        }
467    
468        buffer.append(')');
469      }
470    }