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 }