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