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