001/* 002 * Copyright 2010-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2010-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2010-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.util.args; 037 038 039 040import java.util.Collections; 041import java.util.HashMap; 042import java.util.List; 043import java.util.Map; 044import java.util.concurrent.atomic.AtomicReference; 045 046import com.unboundid.ldap.sdk.SearchScope; 047import com.unboundid.util.Mutable; 048import com.unboundid.util.NotNull; 049import com.unboundid.util.Nullable; 050import com.unboundid.util.StaticUtils; 051import com.unboundid.util.ThreadSafety; 052import com.unboundid.util.ThreadSafetyLevel; 053 054import static com.unboundid.util.args.ArgsMessages.*; 055 056 057 058/** 059 * This class defines an argument that is intended to hold one search scope 060 * values. Scope arguments must take values, and those arguments must represent 061 * valid search scopes. Supported scope values include: 062 * <UL> 063 * <LI>baseObject scope -- base, baseObject, base-object, 0</LI> 064 * <LI>singleLevel scope -- one, singleLevel, single-level, oneLevel, 065 * one-level, 1</LI> 066 * <LI>wholeSubtree scope -- sub, subtree, wholeSubtree, whole-subtree, 2</LI> 067 * <LI>subordinateSubtree scope -- subord, subordinate, subordinates, 068 * subordinateSubtree, subordinate-subtree, 3</LI> 069 * </UL> 070 */ 071@Mutable() 072@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 073public final class ScopeArgument 074 extends Argument 075{ 076 /** 077 * A map of value strings to the corresponding search scopes. 078 */ 079 @NotNull private static final Map<String,SearchScope> SCOPE_STRINGS; 080 081 static 082 { 083 final HashMap<String,SearchScope> scopeMap = 084 new HashMap<>(StaticUtils.computeMapCapacity(21)); 085 086 scopeMap.put("base", SearchScope.BASE); 087 scopeMap.put("baseobject", SearchScope.BASE); 088 scopeMap.put("base-object", SearchScope.BASE); 089 scopeMap.put("0", SearchScope.BASE); 090 091 scopeMap.put("one", SearchScope.ONE); 092 scopeMap.put("singlelevel", SearchScope.ONE); 093 scopeMap.put("single-level", SearchScope.ONE); 094 scopeMap.put("onelevel", SearchScope.ONE); 095 scopeMap.put("one-level", SearchScope.ONE); 096 scopeMap.put("1", SearchScope.ONE); 097 098 scopeMap.put("sub", SearchScope.SUB); 099 scopeMap.put("subtree", SearchScope.SUB); 100 scopeMap.put("wholesubtree", SearchScope.SUB); 101 scopeMap.put("whole-subtree", SearchScope.SUB); 102 scopeMap.put("2", SearchScope.SUB); 103 104 scopeMap.put("subord", SearchScope.SUBORDINATE_SUBTREE); 105 scopeMap.put("subordinate", SearchScope.SUBORDINATE_SUBTREE); 106 scopeMap.put("subordinates", SearchScope.SUBORDINATE_SUBTREE); 107 scopeMap.put("subordinatesubtree", SearchScope.SUBORDINATE_SUBTREE); 108 scopeMap.put("subordinate-subtree", SearchScope.SUBORDINATE_SUBTREE); 109 scopeMap.put("3", SearchScope.SUBORDINATE_SUBTREE); 110 111 SCOPE_STRINGS = Collections.unmodifiableMap(scopeMap); 112 } 113 114 115 116 /** 117 * The serial version UID for this serializable class. 118 */ 119 private static final long serialVersionUID = 5962857448814911423L; 120 121 122 123 // The value assigned to this argument. 124 @NotNull private final AtomicReference<SearchScope> value; 125 126 // The default value for this argument. 127 @Nullable private final SearchScope defaultValue; 128 129 130 131 /** 132 * Creates a new search scope argument with the provided information. It will 133 * not be required, will use a default placeholder, and will not have a 134 * default value. 135 * 136 * @param shortIdentifier The short identifier for this argument. It may 137 * not be {@code null} if the long identifier is 138 * {@code null}. 139 * @param longIdentifier The long identifier for this argument. It may 140 * not be {@code null} if the short identifier is 141 * {@code null}. 142 * @param description A human-readable description for this argument. 143 * It must not be {@code null}. 144 * 145 * @throws ArgumentException If there is a problem with the definition of 146 * this argument. 147 */ 148 public ScopeArgument(@Nullable final Character shortIdentifier, 149 @Nullable final String longIdentifier, 150 @NotNull final String description) 151 throws ArgumentException 152 { 153 this(shortIdentifier, longIdentifier, false, null, description); 154 } 155 156 157 158 /** 159 * Creates a new search scope argument with the provided information. It will 160 * not have a default value. 161 * 162 * @param shortIdentifier The short identifier for this argument. It may 163 * not be {@code null} if the long identifier is 164 * {@code null}. 165 * @param longIdentifier The long identifier for this argument. It may 166 * not be {@code null} if the short identifier is 167 * {@code null}. 168 * @param isRequired Indicates whether this argument is required to 169 * be provided. 170 * @param valuePlaceholder A placeholder to display in usage information to 171 * indicate that a value must be provided. It may 172 * be {@code null} if a default placeholder should 173 * be used. 174 * @param description A human-readable description for this argument. 175 * It must not be {@code null}. 176 * 177 * @throws ArgumentException If there is a problem with the definition of 178 * this argument. 179 */ 180 public ScopeArgument(@Nullable final Character shortIdentifier, 181 @Nullable final String longIdentifier, 182 final boolean isRequired, 183 @Nullable final String valuePlaceholder, 184 @NotNull final String description) 185 throws ArgumentException 186 { 187 this(shortIdentifier, longIdentifier, isRequired, valuePlaceholder, 188 description, null); 189 } 190 191 192 193 /** 194 * Creates a new search scope argument with the provided information. 195 * 196 * @param shortIdentifier The short identifier for this argument. It may 197 * not be {@code null} if the long identifier is 198 * {@code null}. 199 * @param longIdentifier The long identifier for this argument. It may 200 * not be {@code null} if the short identifier is 201 * {@code null}. 202 * @param isRequired Indicates whether this argument is required to 203 * be provided. 204 * @param valuePlaceholder A placeholder to display in usage information to 205 * indicate that a value must be provided. It may 206 * be {@code null} if a default placeholder should 207 * be used. 208 * @param description A human-readable description for this argument. 209 * It must not be {@code null}. 210 * @param defaultValue The default value to use for this argument if no 211 * values were provided. It may be {@code null} if 212 * there should be no default values. 213 * 214 * @throws ArgumentException If there is a problem with the definition of 215 * this argument. 216 */ 217 public ScopeArgument(@Nullable final Character shortIdentifier, 218 @Nullable final String longIdentifier, 219 final boolean isRequired, 220 @Nullable final String valuePlaceholder, 221 @NotNull final String description, 222 @Nullable final SearchScope defaultValue) 223 throws ArgumentException 224 { 225 super(shortIdentifier, longIdentifier, isRequired, 1, 226 (valuePlaceholder == null) 227 ? INFO_PLACEHOLDER_SCOPE.get() 228 : valuePlaceholder, 229 description); 230 231 this.defaultValue = defaultValue; 232 233 value = new AtomicReference<>(); 234 } 235 236 237 238 /** 239 * Creates a new scope argument that is a "clean" copy of the provided 240 * source argument. 241 * 242 * @param source The source argument to use for this argument. 243 */ 244 private ScopeArgument(@NotNull final ScopeArgument source) 245 { 246 super(source); 247 248 defaultValue = source.defaultValue; 249 value = new AtomicReference<>(); 250 } 251 252 253 254 /** 255 * Retrieves the default value for this argument, which will be used if no 256 * value was provided. 257 * 258 * @return The default value for this argument, or {@code null} if there is 259 * no default value. 260 */ 261 @Nullable() 262 public SearchScope getDefaultValue() 263 { 264 return defaultValue; 265 } 266 267 268 269 /** 270 * {@inheritDoc} 271 */ 272 @Override() 273 protected void addValue(@NotNull final String valueString) 274 throws ArgumentException 275 { 276 final SearchScope scope = 277 SCOPE_STRINGS.get(StaticUtils.toLowerCase(valueString)); 278 if (scope == null) 279 { 280 throw new ArgumentException(ERR_SCOPE_VALUE_NOT_VALID.get(valueString, 281 getIdentifierString())); 282 } 283 284 if (! value.compareAndSet(null, scope)) 285 { 286 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 287 getIdentifierString())); 288 } 289 } 290 291 292 293 /** 294 * Retrieves the value for this argument, or the default value if none was 295 * provided. 296 * 297 * @return The value for this argument, or the default value if none was 298 * provided, or {@code null} if there is no value and no default 299 * value. 300 */ 301 @Nullable() 302 public SearchScope getValue() 303 { 304 final SearchScope s = value.get(); 305 if (s == null) 306 { 307 return defaultValue; 308 } 309 else 310 { 311 return s; 312 } 313 } 314 315 316 317 /** 318 * {@inheritDoc} 319 */ 320 @Override() 321 @NotNull() 322 public List<String> getValueStringRepresentations(final boolean useDefault) 323 { 324 SearchScope s = value.get(); 325 if (useDefault && (s == null)) 326 { 327 s = defaultValue; 328 } 329 330 if (s == null) 331 { 332 return Collections.emptyList(); 333 } 334 335 final String scopeStr; 336 switch (s.intValue()) 337 { 338 case SearchScope.BASE_INT_VALUE: 339 scopeStr = "base"; 340 break; 341 case SearchScope.ONE_INT_VALUE: 342 scopeStr = "one"; 343 break; 344 case SearchScope.SUB_INT_VALUE: 345 scopeStr = "sub"; 346 break; 347 case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE: 348 scopeStr = "subordinates"; 349 break; 350 default: 351 scopeStr = s.getName(); 352 break; 353 } 354 355 return Collections.singletonList(scopeStr); 356 } 357 358 359 360 /** 361 * {@inheritDoc} 362 */ 363 @Override() 364 protected boolean hasDefaultValue() 365 { 366 return (defaultValue != null); 367 } 368 369 370 371 /** 372 * {@inheritDoc} 373 */ 374 @Override() 375 @NotNull() 376 public String getDataTypeName() 377 { 378 return INFO_SCOPE_TYPE_NAME.get(); 379 } 380 381 382 383 /** 384 * {@inheritDoc} 385 */ 386 @Override() 387 @NotNull() 388 public String getValueConstraints() 389 { 390 return INFO_SCOPE_CONSTRAINTS.get(); 391 } 392 393 394 395 /** 396 * {@inheritDoc} 397 */ 398 @Override() 399 protected void reset() 400 { 401 super.reset(); 402 value.set(null); 403 } 404 405 406 407 /** 408 * {@inheritDoc} 409 */ 410 @Override() 411 @NotNull() 412 public ScopeArgument getCleanCopy() 413 { 414 return new ScopeArgument(this); 415 } 416 417 418 419 /** 420 * {@inheritDoc} 421 */ 422 @Override() 423 protected void addToCommandLine(@NotNull final List<String> argStrings) 424 { 425 final SearchScope s = value.get(); 426 if (s != null) 427 { 428 if (isSensitive()) 429 { 430 argStrings.add(getIdentifierString()); 431 argStrings.add("***REDACTED***"); 432 return; 433 } 434 435 switch (s.intValue()) 436 { 437 case SearchScope.BASE_INT_VALUE: 438 argStrings.add(getIdentifierString()); 439 argStrings.add("base"); 440 break; 441 case SearchScope.ONE_INT_VALUE: 442 argStrings.add(getIdentifierString()); 443 argStrings.add("one"); 444 break; 445 case SearchScope.SUB_INT_VALUE: 446 argStrings.add(getIdentifierString()); 447 argStrings.add("sub"); 448 break; 449 case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE: 450 argStrings.add(getIdentifierString()); 451 argStrings.add("subordinates"); 452 break; 453 } 454 } 455 } 456 457 458 459 /** 460 * {@inheritDoc} 461 */ 462 @Override() 463 public void toString(@NotNull final StringBuilder buffer) 464 { 465 buffer.append("ScopeArgument("); 466 appendBasicToStringInfo(buffer); 467 468 if (defaultValue != null) 469 { 470 buffer.append(", defaultValue='"); 471 switch (defaultValue.intValue()) 472 { 473 case SearchScope.BASE_INT_VALUE: 474 buffer.append("base"); 475 break; 476 case SearchScope.ONE_INT_VALUE: 477 buffer.append("one"); 478 break; 479 case SearchScope.SUB_INT_VALUE: 480 buffer.append("sub"); 481 break; 482 case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE: 483 buffer.append("subordinate"); 484 break; 485 default: 486 buffer.append(defaultValue.intValue()); 487 break; 488 } 489 buffer.append('\''); 490 } 491 492 buffer.append(')'); 493 } 494}