001/*
002 * Copyright 2010-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2010-2017 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 */
021package com.unboundid.util.args;
022
023
024
025import java.util.Arrays;
026import java.util.Collections;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030import java.util.concurrent.atomic.AtomicReference;
031
032import com.unboundid.ldap.sdk.SearchScope;
033import com.unboundid.util.Mutable;
034import com.unboundid.util.StaticUtils;
035import com.unboundid.util.ThreadSafety;
036import com.unboundid.util.ThreadSafetyLevel;
037
038import 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)
057public 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}