001/* 002 * Copyright 2010-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2010-2018 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 = new HashMap<>(21); 067 068 scopeMap.put("base", SearchScope.BASE); 069 scopeMap.put("baseobject", SearchScope.BASE); 070 scopeMap.put("base-object", SearchScope.BASE); 071 scopeMap.put("0", SearchScope.BASE); 072 073 scopeMap.put("one", SearchScope.ONE); 074 scopeMap.put("singlelevel", SearchScope.ONE); 075 scopeMap.put("single-level", SearchScope.ONE); 076 scopeMap.put("onelevel", SearchScope.ONE); 077 scopeMap.put("one-level", SearchScope.ONE); 078 scopeMap.put("1", SearchScope.ONE); 079 080 scopeMap.put("sub", SearchScope.SUB); 081 scopeMap.put("subtree", SearchScope.SUB); 082 scopeMap.put("wholesubtree", SearchScope.SUB); 083 scopeMap.put("whole-subtree", SearchScope.SUB); 084 scopeMap.put("2", SearchScope.SUB); 085 086 scopeMap.put("subord", SearchScope.SUBORDINATE_SUBTREE); 087 scopeMap.put("subordinate", SearchScope.SUBORDINATE_SUBTREE); 088 scopeMap.put("subordinates", SearchScope.SUBORDINATE_SUBTREE); 089 scopeMap.put("subordinatesubtree", SearchScope.SUBORDINATE_SUBTREE); 090 scopeMap.put("subordinate-subtree", SearchScope.SUBORDINATE_SUBTREE); 091 scopeMap.put("3", SearchScope.SUBORDINATE_SUBTREE); 092 093 SCOPE_STRINGS = Collections.unmodifiableMap(scopeMap); 094 } 095 096 097 098 /** 099 * The serial version UID for this serializable class. 100 */ 101 private static final long serialVersionUID = 5962857448814911423L; 102 103 104 105 // The value assigned to this argument. 106 private final AtomicReference<SearchScope> value; 107 108 // The default value for this argument. 109 private final SearchScope defaultValue; 110 111 112 113 /** 114 * Creates a new search scope argument with the provided information. It will 115 * not be required, will use a default placeholder, and will not have a 116 * default value. 117 * 118 * @param shortIdentifier The short identifier for this argument. It may 119 * not be {@code null} if the long identifier is 120 * {@code null}. 121 * @param longIdentifier The long identifier for this argument. It may 122 * not be {@code null} if the short identifier is 123 * {@code null}. 124 * @param description A human-readable description for this argument. 125 * It must not be {@code null}. 126 * 127 * @throws ArgumentException If there is a problem with the definition of 128 * this argument. 129 */ 130 public ScopeArgument(final Character shortIdentifier, 131 final String longIdentifier, final String description) 132 throws ArgumentException 133 { 134 this(shortIdentifier, longIdentifier, false, null, description); 135 } 136 137 138 139 /** 140 * Creates a new search scope argument with the provided information. It will 141 * not have a default value. 142 * 143 * @param shortIdentifier The short identifier for this argument. It may 144 * not be {@code null} if the long identifier is 145 * {@code null}. 146 * @param longIdentifier The long identifier for this argument. It may 147 * not be {@code null} if the short identifier is 148 * {@code null}. 149 * @param isRequired Indicates whether this argument is required to 150 * be provided. 151 * @param valuePlaceholder A placeholder to display in usage information to 152 * indicate that a value must be provided. It may 153 * be {@code null} if a default placeholder should 154 * be used. 155 * @param description A human-readable description for this argument. 156 * It must not be {@code null}. 157 * 158 * @throws ArgumentException If there is a problem with the definition of 159 * this argument. 160 */ 161 public ScopeArgument(final Character shortIdentifier, 162 final String longIdentifier, final boolean isRequired, 163 final String valuePlaceholder, final String description) 164 throws ArgumentException 165 { 166 this(shortIdentifier, longIdentifier, isRequired, valuePlaceholder, 167 description, null); 168 } 169 170 171 172 /** 173 * Creates a new search scope argument with the provided information. 174 * 175 * @param shortIdentifier The short identifier for this argument. It may 176 * not be {@code null} if the long identifier is 177 * {@code null}. 178 * @param longIdentifier The long identifier for this argument. It may 179 * not be {@code null} if the short identifier is 180 * {@code null}. 181 * @param isRequired Indicates whether this argument is required to 182 * be provided. 183 * @param valuePlaceholder A placeholder to display in usage information to 184 * indicate that a value must be provided. It may 185 * be {@code null} if a default placeholder should 186 * be used. 187 * @param description A human-readable description for this argument. 188 * It must not be {@code null}. 189 * @param defaultValue The default value to use for this argument if no 190 * values were provided. It may be {@code null} if 191 * there should be no default values. 192 * 193 * @throws ArgumentException If there is a problem with the definition of 194 * this argument. 195 */ 196 public ScopeArgument(final Character shortIdentifier, 197 final String longIdentifier, final boolean isRequired, 198 final String valuePlaceholder, final String description, 199 final SearchScope defaultValue) 200 throws ArgumentException 201 { 202 super(shortIdentifier, longIdentifier, isRequired, 1, 203 (valuePlaceholder == null) 204 ? INFO_PLACEHOLDER_SCOPE.get() 205 : valuePlaceholder, 206 description); 207 208 this.defaultValue = defaultValue; 209 210 value = new AtomicReference<>(); 211 } 212 213 214 215 /** 216 * Creates a new scope argument that is a "clean" copy of the provided 217 * source argument. 218 * 219 * @param source The source argument to use for this argument. 220 */ 221 private ScopeArgument(final ScopeArgument source) 222 { 223 super(source); 224 225 defaultValue = source.defaultValue; 226 value = new AtomicReference<>(); 227 } 228 229 230 231 /** 232 * Retrieves the default value for this argument, which will be used if no 233 * value was provided. 234 * 235 * @return The default value for this argument, or {@code null} if there is 236 * no default value. 237 */ 238 public SearchScope getDefaultValue() 239 { 240 return defaultValue; 241 } 242 243 244 245 /** 246 * {@inheritDoc} 247 */ 248 @Override() 249 protected void addValue(final String valueString) 250 throws ArgumentException 251 { 252 final SearchScope scope = 253 SCOPE_STRINGS.get(StaticUtils.toLowerCase(valueString)); 254 if (scope == null) 255 { 256 throw new ArgumentException(ERR_SCOPE_VALUE_NOT_VALID.get(valueString, 257 getIdentifierString())); 258 } 259 260 if (! value.compareAndSet(null, scope)) 261 { 262 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 263 getIdentifierString())); 264 } 265 } 266 267 268 269 /** 270 * Retrieves the value for this argument, or the default value if none was 271 * provided. 272 * 273 * @return The value for this argument, or the default value if none was 274 * provided, or {@code null} if there is no value and no default 275 * value. 276 */ 277 public SearchScope getValue() 278 { 279 final SearchScope s = value.get(); 280 if (s == null) 281 { 282 return defaultValue; 283 } 284 else 285 { 286 return s; 287 } 288 } 289 290 291 292 /** 293 * {@inheritDoc} 294 */ 295 @Override() 296 public List<String> getValueStringRepresentations(final boolean useDefault) 297 { 298 SearchScope s = value.get(); 299 if (useDefault && (s == null)) 300 { 301 s = defaultValue; 302 } 303 304 if (s == null) 305 { 306 return Collections.emptyList(); 307 } 308 309 final String scopeStr; 310 switch (s.intValue()) 311 { 312 case SearchScope.BASE_INT_VALUE: 313 scopeStr = "base"; 314 break; 315 case SearchScope.ONE_INT_VALUE: 316 scopeStr = "one"; 317 break; 318 case SearchScope.SUB_INT_VALUE: 319 scopeStr = "sub"; 320 break; 321 case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE: 322 scopeStr = "subordinates"; 323 break; 324 default: 325 scopeStr = s.getName(); 326 break; 327 } 328 329 return Collections.singletonList(scopeStr); 330 } 331 332 333 334 /** 335 * {@inheritDoc} 336 */ 337 @Override() 338 protected boolean hasDefaultValue() 339 { 340 return (defaultValue != null); 341 } 342 343 344 345 /** 346 * {@inheritDoc} 347 */ 348 @Override() 349 public String getDataTypeName() 350 { 351 return INFO_SCOPE_TYPE_NAME.get(); 352 } 353 354 355 356 /** 357 * {@inheritDoc} 358 */ 359 @Override() 360 public String getValueConstraints() 361 { 362 return INFO_SCOPE_CONSTRAINTS.get(); 363 } 364 365 366 367 /** 368 * {@inheritDoc} 369 */ 370 @Override() 371 protected void reset() 372 { 373 super.reset(); 374 value.set(null); 375 } 376 377 378 379 /** 380 * {@inheritDoc} 381 */ 382 @Override() 383 public ScopeArgument getCleanCopy() 384 { 385 return new ScopeArgument(this); 386 } 387 388 389 390 /** 391 * {@inheritDoc} 392 */ 393 @Override() 394 protected void addToCommandLine(final List<String> argStrings) 395 { 396 final SearchScope s = value.get(); 397 if (s != null) 398 { 399 if (isSensitive()) 400 { 401 argStrings.add(getIdentifierString()); 402 argStrings.add("***REDACTED***"); 403 return; 404 } 405 406 switch (s.intValue()) 407 { 408 case SearchScope.BASE_INT_VALUE: 409 argStrings.add(getIdentifierString()); 410 argStrings.add("base"); 411 break; 412 case SearchScope.ONE_INT_VALUE: 413 argStrings.add(getIdentifierString()); 414 argStrings.add("one"); 415 break; 416 case SearchScope.SUB_INT_VALUE: 417 argStrings.add(getIdentifierString()); 418 argStrings.add("sub"); 419 break; 420 case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE: 421 argStrings.add(getIdentifierString()); 422 argStrings.add("subordinates"); 423 break; 424 } 425 } 426 } 427 428 429 430 /** 431 * {@inheritDoc} 432 */ 433 @Override() 434 public void toString(final StringBuilder buffer) 435 { 436 buffer.append("ScopeArgument("); 437 appendBasicToStringInfo(buffer); 438 439 if (defaultValue != null) 440 { 441 buffer.append(", defaultValue='"); 442 switch (defaultValue.intValue()) 443 { 444 case SearchScope.BASE_INT_VALUE: 445 buffer.append("base"); 446 break; 447 case SearchScope.ONE_INT_VALUE: 448 buffer.append("one"); 449 break; 450 case SearchScope.SUB_INT_VALUE: 451 buffer.append("sub"); 452 break; 453 case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE: 454 buffer.append("subordinate"); 455 break; 456 default: 457 buffer.append(defaultValue.intValue()); 458 break; 459 } 460 buffer.append('\''); 461 } 462 463 buffer.append(')'); 464 } 465}