001/* 002 * Copyright 2008-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-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) 2008-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.ArrayList; 041import java.util.Collections; 042import java.util.Iterator; 043import java.util.List; 044 045import com.unboundid.ldap.sdk.DN; 046import com.unboundid.ldap.sdk.LDAPException; 047import com.unboundid.util.Debug; 048import com.unboundid.util.Mutable; 049import com.unboundid.util.NotNull; 050import com.unboundid.util.Nullable; 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 or more 060 * distinguished name values. DN arguments must take values, and those values 061 * must be able to be parsed as distinguished names. 062 */ 063@Mutable() 064@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 065public final class DNArgument 066 extends Argument 067{ 068 /** 069 * The serial version UID for this serializable class. 070 */ 071 private static final long serialVersionUID = 7956577383262400167L; 072 073 074 075 // The set of values assigned to this argument. 076 @NotNull private final ArrayList<DN> values; 077 078 // The argument value validators that have been registered for this argument. 079 @NotNull private final List<ArgumentValueValidator> validators; 080 081 // The list of default values for this argument. 082 @Nullable private final List<DN> defaultValues; 083 084 085 086 /** 087 * Creates a new DN argument with the provided information. It will not be 088 * required, will permit at most one occurrence, will use a default 089 * placeholder, and will not have a default value. 090 * 091 * @param shortIdentifier The short identifier for this argument. It may 092 * not be {@code null} if the long identifier is 093 * {@code null}. 094 * @param longIdentifier The long identifier for this argument. It may 095 * not be {@code null} if the short identifier is 096 * {@code null}. 097 * @param description A human-readable description for this argument. 098 * It must not be {@code null}. 099 * 100 * @throws ArgumentException If there is a problem with the definition of 101 * this argument. 102 */ 103 public DNArgument(@Nullable final Character shortIdentifier, 104 @Nullable final String longIdentifier, 105 @NotNull final String description) 106 throws ArgumentException 107 { 108 this(shortIdentifier, longIdentifier, false, 1, null, description); 109 } 110 111 112 113 /** 114 * Creates a new DN argument with the provided information. It will not have 115 * a default value. 116 * 117 * @param shortIdentifier The short identifier for this argument. It may 118 * not be {@code null} if the long identifier is 119 * {@code null}. 120 * @param longIdentifier The long identifier for this argument. It may 121 * not be {@code null} if the short identifier is 122 * {@code null}. 123 * @param isRequired Indicates whether this argument is required to 124 * be provided. 125 * @param maxOccurrences The maximum number of times this argument may be 126 * provided on the command line. A value less than 127 * or equal to zero indicates that it may be present 128 * any number of times. 129 * @param valuePlaceholder A placeholder to display in usage information to 130 * indicate that a value must be provided. It may 131 * be {@code null} if a default placeholder should 132 * be used. 133 * @param description A human-readable description for this argument. 134 * It must not be {@code null}. 135 * 136 * @throws ArgumentException If there is a problem with the definition of 137 * this argument. 138 */ 139 public DNArgument(@Nullable final Character shortIdentifier, 140 @Nullable final String longIdentifier, 141 final boolean isRequired, 142 final int maxOccurrences, 143 @Nullable final String valuePlaceholder, 144 @NotNull final String description) 145 throws ArgumentException 146 { 147 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 148 valuePlaceholder, description, (List<DN>) null); 149 } 150 151 152 153 /** 154 * Creates a new DN argument with the provided information. 155 * 156 * @param shortIdentifier The short identifier for this argument. It may 157 * not be {@code null} if the long identifier is 158 * {@code null}. 159 * @param longIdentifier The long identifier for this argument. It may 160 * not be {@code null} if the short identifier is 161 * {@code null}. 162 * @param isRequired Indicates whether this argument is required to 163 * be provided. 164 * @param maxOccurrences The maximum number of times this argument may be 165 * provided on the command line. A value less than 166 * or equal to zero indicates that it may be present 167 * any number of times. 168 * @param valuePlaceholder A placeholder to display in usage information to 169 * indicate that a value must be provided. It may 170 * be {@code null} if a default placeholder should 171 * be used. 172 * @param description A human-readable description for this argument. 173 * It must not be {@code null}. 174 * @param defaultValue The default value to use for this argument if no 175 * values were provided. 176 * 177 * @throws ArgumentException If there is a problem with the definition of 178 * this argument. 179 */ 180 public DNArgument(@Nullable final Character shortIdentifier, 181 @Nullable final String longIdentifier, 182 final boolean isRequired, final int maxOccurrences, 183 @Nullable final String valuePlaceholder, 184 @NotNull final String description, 185 @Nullable final DN defaultValue) 186 throws ArgumentException 187 { 188 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 189 valuePlaceholder, description, 190 ((defaultValue == null) 191 ? null : 192 Collections.singletonList(defaultValue))); 193 } 194 195 196 197 /** 198 * Creates a new DN argument with the provided information. 199 * 200 * @param shortIdentifier The short identifier for this argument. It may 201 * not be {@code null} if the long identifier is 202 * {@code null}. 203 * @param longIdentifier The long identifier for this argument. It may 204 * not be {@code null} if the short identifier is 205 * {@code null}. 206 * @param isRequired Indicates whether this argument is required to 207 * be provided. 208 * @param maxOccurrences The maximum number of times this argument may be 209 * provided on the command line. A value less than 210 * or equal to zero indicates that it may be present 211 * any number of times. 212 * @param valuePlaceholder A placeholder to display in usage information to 213 * indicate that a value must be provided. It may 214 * be {@code null} if a default placeholder should 215 * be used. 216 * @param description A human-readable description for this argument. 217 * It must not be {@code null}. 218 * @param defaultValues The set of default values to use for this 219 * argument if no values were provided. 220 * 221 * @throws ArgumentException If there is a problem with the definition of 222 * this argument. 223 */ 224 public DNArgument(@Nullable final Character shortIdentifier, 225 @Nullable final String longIdentifier, 226 final boolean isRequired, final int maxOccurrences, 227 @Nullable final String valuePlaceholder, 228 @NotNull final String description, 229 @Nullable final List<DN> defaultValues) 230 throws ArgumentException 231 { 232 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 233 (valuePlaceholder == null) 234 ? INFO_PLACEHOLDER_DN.get() 235 : valuePlaceholder, 236 description); 237 238 if ((defaultValues == null) || defaultValues.isEmpty()) 239 { 240 this.defaultValues = null; 241 } 242 else 243 { 244 this.defaultValues = Collections.unmodifiableList(defaultValues); 245 } 246 247 values = new ArrayList<>(5); 248 validators = new ArrayList<>(5); 249 } 250 251 252 253 /** 254 * Creates a new DN argument that is a "clean" copy of the provided source 255 * argument. 256 * 257 * @param source The source argument to use for this argument. 258 */ 259 private DNArgument(@NotNull final DNArgument source) 260 { 261 super(source); 262 263 defaultValues = source.defaultValues; 264 values = new ArrayList<>(5); 265 validators = new ArrayList<>(source.validators); 266 } 267 268 269 270 /** 271 * Retrieves the list of default values for this argument, which will be used 272 * if no values were provided. 273 * 274 * @return The list of default values for this argument, or {@code null} if 275 * there are no default values. 276 */ 277 @Nullable() 278 public List<DN> getDefaultValues() 279 { 280 return defaultValues; 281 } 282 283 284 285 /** 286 * Updates this argument to ensure that the provided validator will be invoked 287 * for any values provided to this argument. This validator will be invoked 288 * after all other validation has been performed for this argument. 289 * 290 * @param validator The argument value validator to be invoked. It must not 291 * be {@code null}. 292 */ 293 public void addValueValidator(@NotNull final ArgumentValueValidator validator) 294 { 295 validators.add(validator); 296 } 297 298 299 300 /** 301 * {@inheritDoc} 302 */ 303 @Override() 304 protected void addValue(@NotNull final String valueString) 305 throws ArgumentException 306 { 307 final DN parsedDN; 308 try 309 { 310 parsedDN = new DN(valueString); 311 } 312 catch (final LDAPException le) 313 { 314 Debug.debugException(le); 315 throw new ArgumentException(ERR_DN_VALUE_NOT_DN.get(valueString, 316 getIdentifierString(), le.getMessage()), 317 le); 318 } 319 320 if (values.size() >= getMaxOccurrences()) 321 { 322 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 323 getIdentifierString())); 324 } 325 326 for (final ArgumentValueValidator v : validators) 327 { 328 v.validateArgumentValue(this, valueString); 329 } 330 331 values.add(parsedDN); 332 } 333 334 335 336 /** 337 * Retrieves the value for this argument, or the default value if none was 338 * provided. If there are multiple values, then the first will be returned. 339 * 340 * @return The value for this argument, or the default value if none was 341 * provided, or {@code null} if there is no value and no default 342 * value. 343 */ 344 @Nullable() 345 public DN getValue() 346 { 347 if (values.isEmpty()) 348 { 349 if ((defaultValues == null) || defaultValues.isEmpty()) 350 { 351 return null; 352 } 353 else 354 { 355 return defaultValues.get(0); 356 } 357 } 358 else 359 { 360 return values.get(0); 361 } 362 } 363 364 365 366 /** 367 * Retrieves the set of values for this argument. 368 * 369 * @return The set of values for this argument. 370 */ 371 @NotNull() 372 public List<DN> getValues() 373 { 374 if (values.isEmpty() && (defaultValues != null)) 375 { 376 return defaultValues; 377 } 378 379 return Collections.unmodifiableList(values); 380 } 381 382 383 384 /** 385 * Retrieves a string representation of the value for this argument, or a 386 * string representation of the default value if none was provided. If there 387 * are multiple values, then the first will be returned. 388 * 389 * @return The string representation of the value for this argument, or the 390 * string representation of the default value if none was provided, 391 * or {@code null} if there is no value and no default value. 392 */ 393 @Nullable() 394 public String getStringValue() 395 { 396 final DN valueDN = getValue(); 397 if (valueDN == null) 398 { 399 return null; 400 } 401 402 return valueDN.toString(); 403 } 404 405 406 407 /** 408 * {@inheritDoc} 409 */ 410 @Override() 411 @NotNull() 412 public List<String> getValueStringRepresentations(final boolean useDefault) 413 { 414 if (values.isEmpty()) 415 { 416 if (useDefault && (defaultValues != null)) 417 { 418 final ArrayList<String> valueStrings = 419 new ArrayList<>(defaultValues.size()); 420 for (final DN dn : defaultValues) 421 { 422 valueStrings.add(dn.toString()); 423 } 424 return Collections.unmodifiableList(valueStrings); 425 } 426 else 427 { 428 return Collections.emptyList(); 429 } 430 } 431 else 432 { 433 final ArrayList<String> valueStrings = new ArrayList<>(values.size()); 434 for (final DN dn : values) 435 { 436 valueStrings.add(dn.toString()); 437 } 438 return Collections.unmodifiableList(valueStrings); 439 } 440 } 441 442 443 444 /** 445 * {@inheritDoc} 446 */ 447 @Override() 448 protected boolean hasDefaultValue() 449 { 450 return ((defaultValues != null) && (! defaultValues.isEmpty())); 451 } 452 453 454 455 /** 456 * {@inheritDoc} 457 */ 458 @Override() 459 @NotNull() 460 public String getDataTypeName() 461 { 462 return INFO_DN_TYPE_NAME.get(); 463 } 464 465 466 467 /** 468 * {@inheritDoc} 469 */ 470 @Override() 471 @NotNull() 472 public String getValueConstraints() 473 { 474 return INFO_DN_CONSTRAINTS.get(); 475 } 476 477 478 479 /** 480 * {@inheritDoc} 481 */ 482 @Override() 483 protected void reset() 484 { 485 super.reset(); 486 values.clear(); 487 } 488 489 490 491 /** 492 * {@inheritDoc} 493 */ 494 @Override() 495 @NotNull() 496 public DNArgument getCleanCopy() 497 { 498 return new DNArgument(this); 499 } 500 501 502 503 /** 504 * {@inheritDoc} 505 */ 506 @Override() 507 protected void addToCommandLine(@NotNull final List<String> argStrings) 508 { 509 for (final DN dn : values) 510 { 511 argStrings.add(getIdentifierString()); 512 if (isSensitive()) 513 { 514 argStrings.add("***REDACTED***"); 515 } 516 else 517 { 518 argStrings.add(String.valueOf(dn)); 519 } 520 } 521 } 522 523 524 525 /** 526 * {@inheritDoc} 527 */ 528 @Override() 529 public void toString(@NotNull final StringBuilder buffer) 530 { 531 buffer.append("DNArgument("); 532 appendBasicToStringInfo(buffer); 533 534 if ((defaultValues != null) && (! defaultValues.isEmpty())) 535 { 536 if (defaultValues.size() == 1) 537 { 538 buffer.append(", defaultValue='"); 539 buffer.append(defaultValues.get(0).toString()); 540 } 541 else 542 { 543 buffer.append(", defaultValues={"); 544 545 final Iterator<DN> iterator = defaultValues.iterator(); 546 while (iterator.hasNext()) 547 { 548 buffer.append('\''); 549 buffer.append(iterator.next().toString()); 550 buffer.append('\''); 551 552 if (iterator.hasNext()) 553 { 554 buffer.append(", "); 555 } 556 } 557 558 buffer.append('}'); 559 } 560 } 561 562 buffer.append(')'); 563 } 564}