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