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