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}