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}