001/* 002 * Copyright 2015-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2015-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) 2015-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.HashMap; 043import java.util.Iterator; 044import java.util.List; 045import java.util.Map; 046 047import com.unboundid.asn1.ASN1OctetString; 048import com.unboundid.ldap.sdk.Control; 049import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl; 050import com.unboundid.ldap.sdk.controls.DontUseCopyRequestControl; 051import com.unboundid.ldap.sdk.controls.DraftLDUPSubentriesRequestControl; 052import com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl; 053import com.unboundid.ldap.sdk.controls.PermissiveModifyRequestControl; 054import com.unboundid.ldap.sdk.controls.SubtreeDeleteRequestControl; 055import com.unboundid.ldap.sdk.experimental. 056 DraftBeheraLDAPPasswordPolicy10RequestControl; 057import com.unboundid.ldap.sdk.experimental. 058 DraftZeilengaLDAPNoOp12RequestControl; 059import com.unboundid.util.Base64; 060import com.unboundid.util.Debug; 061import com.unboundid.util.Mutable; 062import com.unboundid.util.NotNull; 063import com.unboundid.util.Nullable; 064import com.unboundid.util.StaticUtils; 065import com.unboundid.util.ThreadSafety; 066import com.unboundid.util.ThreadSafetyLevel; 067 068import static com.unboundid.util.args.ArgsMessages.*; 069 070 071 072/** 073 * This class defines an argument that is intended to hold information about one 074 * or more LDAP controls. Values for this argument must be in one of the 075 * following formats: 076 * <UL> 077 * <LI> 078 * oid -- The numeric OID for the control. The control will not be critical 079 * and will not have a value. 080 * </LI> 081 * <LI> 082 * oid:criticality -- The numeric OID followed by a colon and the 083 * criticality. The control will be critical if the criticality value is 084 * any of the following: {@code true}, {@code t}, {@code yes}, {@code y}, 085 * {@code on}, or {@code 1}. The control will be non-critical if the 086 * criticality value is any of the following: {@code false}, {@code f}, 087 * {@code no}, {@code n}, {@code off}, or {@code 0}. No other criticality 088 * values will be accepted. 089 * </LI> 090 * <LI> 091 * oid:criticality:value -- The numeric OID followed by a colon and the 092 * criticality, then a colon and then a string that represents the value for 093 * the control. 094 * </LI> 095 * <LI> 096 * oid:criticality::base64value -- The numeric OID followed by a colon and 097 * the criticality, then two colons and then a string that represents the 098 * base64-encoded value for the control. 099 * </LI> 100 * </UL> 101 */ 102@Mutable() 103@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 104public final class ControlArgument 105 extends Argument 106{ 107 /** 108 * A map of human-readable names to the corresponding numeric OIDs. 109 */ 110 @NotNull private static final Map<String,String> OIDS_BY_NAME; 111 static 112 { 113 final HashMap<String,String> oidsByName = 114 new HashMap<>(StaticUtils.computeMapCapacity(100)); 115 116 // The authorization identity request control. 117 oidsByName.put("authzid", 118 AuthorizationIdentityRequestControl. 119 AUTHORIZATION_IDENTITY_REQUEST_OID); 120 oidsByName.put("authorizationidentity", 121 AuthorizationIdentityRequestControl. 122 AUTHORIZATION_IDENTITY_REQUEST_OID); 123 oidsByName.put("authorization-identity", 124 AuthorizationIdentityRequestControl. 125 AUTHORIZATION_IDENTITY_REQUEST_OID); 126 127 // The don't use copy request control. 128 oidsByName.put("nocopy", 129 DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID); 130 oidsByName.put("dontusecopy", 131 DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID); 132 oidsByName.put("no-copy", 133 DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID); 134 oidsByName.put("dont-use-copy", 135 DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID); 136 137 // The LDAP no-operation request control. 138 oidsByName.put("noop", 139 DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID); 140 oidsByName.put("nooperation", 141 DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID); 142 oidsByName.put("no-op", 143 DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID); 144 oidsByName.put("no-operation", 145 DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID); 146 147 // The LDAP subentries request control as described in 148 // draft-ietf-ldup-subentry. 149 oidsByName.put("subentries", 150 DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID); 151 oidsByName.put("ldapsubentries", 152 DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID); 153 oidsByName.put("ldap-subentries", 154 DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID); 155 oidsByName.put("ldupsubentries", 156 DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID); 157 oidsByName.put("ldup-subentries", 158 DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID); 159 oidsByName.put("draftldupsubentries", 160 DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID); 161 oidsByName.put("draft-ldup-subentries", 162 DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID); 163 oidsByName.put("draftietfldupsubentries", 164 DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID); 165 oidsByName.put("draft-ietf-ldup-subentries", 166 DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID); 167 168 // The manage DSA IT request control. 169 oidsByName.put("managedsait", 170 ManageDsaITRequestControl.MANAGE_DSA_IT_REQUEST_OID); 171 oidsByName.put("manage-dsa-it", 172 ManageDsaITRequestControl.MANAGE_DSA_IT_REQUEST_OID); 173 174 // The permissive modify request control. 175 oidsByName.put("permissivemodify", 176 PermissiveModifyRequestControl.PERMISSIVE_MODIFY_REQUEST_OID); 177 oidsByName.put("permissive-modify", 178 PermissiveModifyRequestControl.PERMISSIVE_MODIFY_REQUEST_OID); 179 180 // The password policy request control. 181 oidsByName.put("pwpolicy", 182 DraftBeheraLDAPPasswordPolicy10RequestControl. 183 PASSWORD_POLICY_REQUEST_OID); 184 oidsByName.put("passwordpolicy", 185 DraftBeheraLDAPPasswordPolicy10RequestControl. 186 PASSWORD_POLICY_REQUEST_OID); 187 oidsByName.put("pw-policy", 188 DraftBeheraLDAPPasswordPolicy10RequestControl. 189 PASSWORD_POLICY_REQUEST_OID); 190 oidsByName.put("password-policy", 191 DraftBeheraLDAPPasswordPolicy10RequestControl. 192 PASSWORD_POLICY_REQUEST_OID); 193 194 // The subtree delete request control. 195 oidsByName.put("subtreedelete", 196 SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID); 197 oidsByName.put("treedelete", 198 SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID); 199 oidsByName.put("subtree-delete", 200 SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID); 201 oidsByName.put("tree-delete", 202 SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID); 203 204 // The account usable request control. 205 oidsByName.put("accountusable", "1.3.6.1.4.1.42.2.27.9.5.8"); 206 oidsByName.put("accountusability", "1.3.6.1.4.1.42.2.27.9.5.8"); 207 oidsByName.put("account-usable", "1.3.6.1.4.1.42.2.27.9.5.8"); 208 oidsByName.put("account-usability", "1.3.6.1.4.1.42.2.27.9.5.8"); 209 210 // The generate password request control. 211 oidsByName.put("generatepassword", "1.3.6.1.4.1.30221.2.5.58"); 212 oidsByName.put("generate-password", "1.3.6.1.4.1.30221.2.5.58"); 213 oidsByName.put("generatepw", "1.3.6.1.4.1.30221.2.5.58"); 214 oidsByName.put("generate-pw", "1.3.6.1.4.1.30221.2.5.58"); 215 216 // The get backend set ID request control. 217 oidsByName.put("backendsetid", "1.3.6.1.4.1.30221.2.5.33"); 218 oidsByName.put("getbackendsetid", "1.3.6.1.4.1.30221.2.5.33"); 219 oidsByName.put("backendset-id", "1.3.6.1.4.1.30221.2.5.33"); 220 oidsByName.put("backend-set-id", "1.3.6.1.4.1.30221.2.5.33"); 221 oidsByName.put("get-backendset-id", "1.3.6.1.4.1.30221.2.5.33"); 222 oidsByName.put("get-backend-set-id", "1.3.6.1.4.1.30221.2.5.33"); 223 224 // The get effective rights request control. 225 oidsByName.put("effectiverights", "1.3.6.1.4.1.42.2.27.9.5.2"); 226 oidsByName.put("geteffectiverights", "1.3.6.1.4.1.42.2.27.9.5.2"); 227 oidsByName.put("effective-rights", "1.3.6.1.4.1.42.2.27.9.5.2"); 228 oidsByName.put("get-effective-rights", "1.3.6.1.4.1.42.2.27.9.5.2"); 229 230 // The get password policy state issues request control. 231 oidsByName.put("pwpolicystateissues", "1.3.6.1.4.1.30221.2.5.46"); 232 oidsByName.put("getpwpolicystateissues", "1.3.6.1.4.1.30221.2.5.46"); 233 oidsByName.put("passwordpolicystateissues", "1.3.6.1.4.1.30221.2.5.46"); 234 oidsByName.put("getpasswordpolicystateissues", "1.3.6.1.4.1.30221.2.5.46"); 235 oidsByName.put("pw-policy-state-issues", "1.3.6.1.4.1.30221.2.5.46"); 236 oidsByName.put("get-pw-policy-state-issues", "1.3.6.1.4.1.30221.2.5.46"); 237 oidsByName.put("password-policy-state-issues", "1.3.6.1.4.1.30221.2.5.46"); 238 oidsByName.put("get-password-policy-state-issues", 239 "1.3.6.1.4.1.30221.2.5.46"); 240 241 // The get recent login history request control. 242 oidsByName.put("loginhistory", "1.3.6.1.4.1.30221.2.5.61"); 243 oidsByName.put("recentloginhistory", "1.3.6.1.4.1.30221.2.5.61"); 244 oidsByName.put("getrecentloginhistory", "1.3.6.1.4.1.30221.2.5.61"); 245 oidsByName.put("login-history", "1.3.6.1.4.1.30221.2.5.61"); 246 oidsByName.put("recent-login-history", "1.3.6.1.4.1.30221.2.5.61"); 247 oidsByName.put("get-recent-login-history", "1.3.6.1.4.1.30221.2.5.61"); 248 249 // The get server ID request control. 250 oidsByName.put("serverid", "1.3.6.1.4.1.30221.2.5.14"); 251 oidsByName.put("getserverid", "1.3.6.1.4.1.30221.2.5.14"); 252 oidsByName.put("server-id", "1.3.6.1.4.1.30221.2.5.14"); 253 oidsByName.put("get-server-id", "1.3.6.1.4.1.30221.2.5.14"); 254 255 // The get user resource limits request control. 256 oidsByName.put("userresourcelimits", "1.3.6.1.4.1.30221.2.5.25"); 257 oidsByName.put("getuserresourcelimits", "1.3.6.1.4.1.30221.2.5.25"); 258 oidsByName.put("user-resource-limits", "1.3.6.1.4.1.30221.2.5.25"); 259 oidsByName.put("get-user-resource-limits", "1.3.6.1.4.1.30221.2.5.25"); 260 261 // The hard delete request control. 262 oidsByName.put("harddelete", "1.3.6.1.4.1.30221.2.5.22"); 263 oidsByName.put("hard-delete", "1.3.6.1.4.1.30221.2.5.22"); 264 265 // The ignore NO-USER-MODIFICATION request control. 266 oidsByName.put("ignorenousermod", "1.3.6.1.4.1.30221.2.5.5"); 267 oidsByName.put("ignorenousermodification", "1.3.6.1.4.1.30221.2.5.5"); 268 oidsByName.put("ignore-no-user-mod", "1.3.6.1.4.1.30221.2.5.5"); 269 oidsByName.put("ignore-no-user-modification", "1.3.6.1.4.1.30221.2.5.5"); 270 271 // The purge retired password request control. 272 oidsByName.put("purgepassword", "1.3.6.1.4.1.30221.2.5.32"); 273 oidsByName.put("purgeretiredpassword", "1.3.6.1.4.1.30221.2.5.32"); 274 oidsByName.put("purge-password", "1.3.6.1.4.1.30221.2.5.32"); 275 oidsByName.put("purge-retired-password", "1.3.6.1.4.1.30221.2.5.32"); 276 277 // The real attributes only request control. 278 oidsByName.put("realattrsonly", "2.16.840.1.113730.3.4.17"); 279 oidsByName.put("realattributesonly", "2.16.840.1.113730.3.4.17"); 280 oidsByName.put("real-attrs-only", "2.16.840.1.113730.3.4.17"); 281 oidsByName.put("real-attributes-only", "2.16.840.1.113730.3.4.17"); 282 283 // The replication repair request control. 284 oidsByName.put("replrepair", "1.3.6.1.4.1.30221.1.5.2"); 285 oidsByName.put("replicationrepair", "1.3.6.1.4.1.30221.1.5.2"); 286 oidsByName.put("repl-repair", "1.3.6.1.4.1.30221.1.5.2"); 287 oidsByName.put("replication-repair", "1.3.6.1.4.1.30221.1.5.2"); 288 289 // The retain identity request control. 290 oidsByName.put("retainidentity", "1.3.6.1.4.1.30221.2.5.3"); 291 oidsByName.put("retain-identity", "1.3.6.1.4.1.30221.2.5.3"); 292 293 // The retire password request control. 294 oidsByName.put("retirepassword", "1.3.6.1.4.1.30221.2.5.31"); 295 oidsByName.put("retire-password", "1.3.6.1.4.1.30221.2.5.31"); 296 297 // The return conflict entries request control. 298 oidsByName.put("returnconflictentries", "1.3.6.1.4.1.30221.2.5.13"); 299 oidsByName.put("return-conflict-entries", "1.3.6.1.4.1.30221.2.5.13"); 300 301 // The soft delete request control. 302 oidsByName.put("softdelete", "1.3.6.1.4.1.30221.2.5.20"); 303 oidsByName.put("soft-delete", "1.3.6.1.4.1.30221.2.5.20"); 304 305 // The soft-deleted entry access request control. 306 oidsByName.put("softdeleteentryaccess", "1.3.6.1.4.1.30221.2.5.24"); 307 oidsByName.put("softdeletedentryaccess", "1.3.6.1.4.1.30221.2.5.24"); 308 oidsByName.put("soft-delete-entry-access", "1.3.6.1.4.1.30221.2.5.24"); 309 oidsByName.put("soft-deleted-entry-access", "1.3.6.1.4.1.30221.2.5.24"); 310 311 // The suppress referential integrity updates request control. 312 oidsByName.put("suppressreferentialintegrity", "1.3.6.1.4.1.30221.2.5.30"); 313 oidsByName.put("suppressreferentialintegrityupdates", 314 "1.3.6.1.4.1.30221.2.5.30"); 315 oidsByName.put("suppress-referential-integrity", 316 "1.3.6.1.4.1.30221.2.5.30"); 317 oidsByName.put("suppress-referential-integrity-updates", 318 "1.3.6.1.4.1.30221.2.5.30"); 319 320 // The undelete request control. 321 oidsByName.put("undelete", "1.3.6.1.4.1.30221.2.5.23"); 322 323 // The virtual attributes only request control. 324 oidsByName.put("virtualattrsonly", "2.16.840.1.113730.3.4.19"); 325 oidsByName.put("virtualattributesonly", "2.16.840.1.113730.3.4.19"); 326 oidsByName.put("virtual-attrs-only", "2.16.840.1.113730.3.4.19"); 327 oidsByName.put("virtual-attributes-only", "2.16.840.1.113730.3.4.19"); 328 329 OIDS_BY_NAME = Collections.unmodifiableMap(oidsByName); 330 } 331 332 333 334 /** 335 * The serial version UID for this serializable class. 336 */ 337 private static final long serialVersionUID = -1889200072476038957L; 338 339 340 341 // The argument value validators that have been registered for this argument. 342 @NotNull private final List<ArgumentValueValidator> validators; 343 344 // The list of default values for this argument. 345 @Nullable private final List<Control> defaultValues; 346 347 // The set of values assigned to this argument. 348 @NotNull private final List<Control> values; 349 350 351 352 /** 353 * Creates a new control argument with the provided information. It will not 354 * be required, will be allowed any number of times, will use a default 355 * placeholder, and will not have a default value. 356 * 357 * @param shortIdentifier The short identifier for this argument. It may 358 * not be {@code null} if the long identifier is 359 * {@code null}. 360 * @param longIdentifier The long identifier for this argument. It may 361 * not be {@code null} if the short identifier is 362 * {@code null}. 363 * @param description A human-readable description for this argument. 364 * It must not be {@code null}. 365 * 366 * @throws ArgumentException If there is a problem with the definition of 367 * this argument. 368 */ 369 public ControlArgument(@Nullable final Character shortIdentifier, 370 @Nullable final String longIdentifier, 371 @NotNull final String description) 372 throws ArgumentException 373 { 374 this(shortIdentifier, longIdentifier, false, 0, null, description); 375 } 376 377 378 379 /** 380 * Creates a new control argument with the provided information. It will not 381 * have a default value. 382 * 383 * @param shortIdentifier The short identifier for this argument. It may 384 * not be {@code null} if the long identifier is 385 * {@code null}. 386 * @param longIdentifier The long identifier for this argument. It may 387 * not be {@code null} if the short identifier is 388 * {@code null}. 389 * @param isRequired Indicates whether this argument is required to 390 * be provided. 391 * @param maxOccurrences The maximum number of times this argument may be 392 * provided on the command line. A value less than 393 * or equal to zero indicates that it may be present 394 * any number of times. 395 * @param valuePlaceholder A placeholder to display in usage information to 396 * indicate that a value must be provided. It may 397 * be {@code null} to use a default placeholder that 398 * describes the expected syntax for values. 399 * @param description A human-readable description for this argument. 400 * It must not be {@code null}. 401 * 402 * @throws ArgumentException If there is a problem with the definition of 403 * this argument. 404 */ 405 public ControlArgument(@Nullable final Character shortIdentifier, 406 @Nullable final String longIdentifier, 407 final boolean isRequired, 408 final int maxOccurrences, 409 @Nullable final String valuePlaceholder, 410 @NotNull final String description) 411 throws ArgumentException 412 { 413 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 414 valuePlaceholder, description, (List<Control>) null); 415 } 416 417 418 419 /** 420 * Creates a new control argument with the provided information. 421 * 422 * @param shortIdentifier The short identifier for this argument. It may 423 * not be {@code null} if the long identifier is 424 * {@code null}. 425 * @param longIdentifier The long identifier for this argument. It may 426 * not be {@code null} if the short identifier is 427 * {@code null}. 428 * @param isRequired Indicates whether this argument is required to 429 * be provided. 430 * @param maxOccurrences The maximum number of times this argument may be 431 * provided on the command line. A value less than 432 * or equal to zero indicates that it may be present 433 * any number of times. 434 * @param valuePlaceholder A placeholder to display in usage information to 435 * indicate that a value must be provided. It may 436 * be {@code null} to use a default placeholder that 437 * describes the expected syntax for values. 438 * @param description A human-readable description for this argument. 439 * It must not be {@code null}. 440 * @param defaultValue The default value to use for this argument if no 441 * values were provided. It may be {@code null} if 442 * there should be no default values. 443 * 444 * @throws ArgumentException If there is a problem with the definition of 445 * this argument. 446 */ 447 public ControlArgument(@Nullable final Character shortIdentifier, 448 @Nullable final String longIdentifier, 449 final boolean isRequired, 450 final int maxOccurrences, 451 @Nullable final String valuePlaceholder, 452 @NotNull final String description, 453 @Nullable final Control defaultValue) 454 throws ArgumentException 455 { 456 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 457 valuePlaceholder, description, 458 ((defaultValue == null) 459 ? null : 460 Collections.singletonList(defaultValue))); 461 } 462 463 464 465 /** 466 * Creates a new control argument with the provided information. 467 * 468 * @param shortIdentifier The short identifier for this argument. It may 469 * not be {@code null} if the long identifier is 470 * {@code null}. 471 * @param longIdentifier The long identifier for this argument. It may 472 * not be {@code null} if the short identifier is 473 * {@code null}. 474 * @param isRequired Indicates whether this argument is required to 475 * be provided. 476 * @param maxOccurrences The maximum number of times this argument may be 477 * provided on the command line. A value less than 478 * or equal to zero indicates that it may be present 479 * any number of times. 480 * @param valuePlaceholder A placeholder to display in usage information to 481 * indicate that a value must be provided. It may 482 * be {@code null} to use a default placeholder that 483 * describes the expected syntax for values. 484 * @param description A human-readable description for this argument. 485 * It must not be {@code null}. 486 * @param defaultValues The set of default values to use for this 487 * argument if no values were provided. 488 * 489 * @throws ArgumentException If there is a problem with the definition of 490 * this argument. 491 */ 492 public ControlArgument(@Nullable final Character shortIdentifier, 493 @Nullable final String longIdentifier, 494 final boolean isRequired, 495 final int maxOccurrences, 496 @Nullable final String valuePlaceholder, 497 @NotNull final String description, 498 @Nullable final List<Control> defaultValues) 499 throws ArgumentException 500 { 501 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 502 (valuePlaceholder == null) 503 ? INFO_PLACEHOLDER_CONTROL.get() 504 : valuePlaceholder, 505 description); 506 507 if ((defaultValues == null) || defaultValues.isEmpty()) 508 { 509 this.defaultValues = null; 510 } 511 else 512 { 513 this.defaultValues = Collections.unmodifiableList(defaultValues); 514 } 515 516 values = new ArrayList<>(5); 517 validators = new ArrayList<>(5); 518 } 519 520 521 522 /** 523 * Creates a new control argument that is a "clean" copy of the provided 524 * source argument. 525 * 526 * @param source The source argument to use for this argument. 527 */ 528 private ControlArgument(@NotNull final ControlArgument source) 529 { 530 super(source); 531 532 defaultValues = source.defaultValues; 533 validators = new ArrayList<>(source.validators); 534 values = new ArrayList<>(5); 535 } 536 537 538 539 /** 540 * Retrieves the list of default values for this argument, which will be used 541 * if no values were provided. 542 * 543 * @return The list of default values for this argument, or {@code null} if 544 * there are no default values. 545 */ 546 @Nullable() 547 public List<Control> getDefaultValues() 548 { 549 return defaultValues; 550 } 551 552 553 554 /** 555 * Updates this argument to ensure that the provided validator will be invoked 556 * for any values provided to this argument. This validator will be invoked 557 * after all other validation has been performed for this argument. 558 * 559 * @param validator The argument value validator to be invoked. It must not 560 * be {@code null}. 561 */ 562 public void addValueValidator(@NotNull final ArgumentValueValidator validator) 563 { 564 validators.add(validator); 565 } 566 567 568 569 /** 570 * {@inheritDoc} 571 */ 572 @Override() 573 protected void addValue(@NotNull final String valueString) 574 throws ArgumentException 575 { 576 String oid = null; 577 boolean isCritical = false; 578 ASN1OctetString value = null; 579 580 final int firstColonPos = valueString.indexOf(':'); 581 if (firstColonPos < 0) 582 { 583 oid = valueString; 584 } 585 else 586 { 587 oid = valueString.substring(0, firstColonPos); 588 589 final String criticalityStr; 590 final int secondColonPos = valueString.indexOf(':', (firstColonPos+1)); 591 if (secondColonPos < 0) 592 { 593 criticalityStr = valueString.substring(firstColonPos+1); 594 } 595 else 596 { 597 criticalityStr = valueString.substring(firstColonPos+1, secondColonPos); 598 599 final int doubleColonPos = valueString.indexOf("::"); 600 if (doubleColonPos == secondColonPos) 601 { 602 try 603 { 604 value = new ASN1OctetString( 605 Base64.decode(valueString.substring(doubleColonPos+2))); 606 } 607 catch (final Exception e) 608 { 609 Debug.debugException(e); 610 throw new ArgumentException( 611 ERR_CONTROL_ARG_INVALID_BASE64_VALUE.get(valueString, 612 getIdentifierString(), 613 valueString.substring(doubleColonPos+2)), 614 e); 615 } 616 } 617 else 618 { 619 value = new ASN1OctetString(valueString.substring(secondColonPos+1)); 620 } 621 } 622 623 final String lowerCriticalityStr = 624 StaticUtils.toLowerCase(criticalityStr); 625 if (lowerCriticalityStr.equals("true") || 626 lowerCriticalityStr.equals("t") || 627 lowerCriticalityStr.equals("yes") || 628 lowerCriticalityStr.equals("y") || 629 lowerCriticalityStr.equals("on") || 630 lowerCriticalityStr.equals("1")) 631 { 632 isCritical = true; 633 } 634 else if (lowerCriticalityStr.equals("false") || 635 lowerCriticalityStr.equals("f") || 636 lowerCriticalityStr.equals("no") || 637 lowerCriticalityStr.equals("n") || 638 lowerCriticalityStr.equals("off") || 639 lowerCriticalityStr.equals("0")) 640 { 641 isCritical = false; 642 } 643 else 644 { 645 throw new ArgumentException(ERR_CONTROL_ARG_INVALID_CRITICALITY.get( 646 valueString, getIdentifierString(), criticalityStr)); 647 } 648 } 649 650 if (! StaticUtils.isNumericOID(oid)) 651 { 652 final String providedOID = oid; 653 oid = OIDS_BY_NAME.get(StaticUtils.toLowerCase(providedOID)); 654 if (oid == null) 655 { 656 throw new ArgumentException(ERR_CONTROL_ARG_INVALID_OID.get( 657 valueString, getIdentifierString(), providedOID)); 658 } 659 } 660 661 if (values.size() >= getMaxOccurrences()) 662 { 663 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 664 getIdentifierString())); 665 } 666 667 for (final ArgumentValueValidator v : validators) 668 { 669 v.validateArgumentValue(this, valueString); 670 } 671 672 values.add(new Control(oid, isCritical, value)); 673 } 674 675 676 677 /** 678 * Retrieves the value for this argument, or the default value if none was 679 * provided. If there are multiple values, then the first will be returned. 680 * 681 * @return The value for this argument, or the default value if none was 682 * provided, or {@code null} if there is no value and no default 683 * value. 684 */ 685 @Nullable() 686 public Control getValue() 687 { 688 if (values.isEmpty()) 689 { 690 if ((defaultValues == null) || defaultValues.isEmpty()) 691 { 692 return null; 693 } 694 else 695 { 696 return defaultValues.get(0); 697 } 698 } 699 else 700 { 701 return values.get(0); 702 } 703 } 704 705 706 707 /** 708 * Retrieves the set of values for this argument, or the default values if 709 * none were provided. 710 * 711 * @return The set of values for this argument, or the default values if none 712 * were provided. 713 */ 714 @NotNull() 715 public List<Control> getValues() 716 { 717 if (values.isEmpty() && (defaultValues != null)) 718 { 719 return defaultValues; 720 } 721 722 return Collections.unmodifiableList(values); 723 } 724 725 726 727 /** 728 * {@inheritDoc} 729 */ 730 @Override() 731 @NotNull() 732 public List<String> getValueStringRepresentations(final boolean useDefault) 733 { 734 final List<Control> controls; 735 if (values.isEmpty()) 736 { 737 if (useDefault) 738 { 739 controls = defaultValues; 740 } 741 else 742 { 743 return Collections.emptyList(); 744 } 745 } 746 else 747 { 748 controls = values; 749 } 750 751 if ((controls == null) || controls.isEmpty()) 752 { 753 return Collections.emptyList(); 754 } 755 756 final StringBuilder buffer = new StringBuilder(); 757 final ArrayList<String> valueStrings = new ArrayList<>(controls.size()); 758 for (final Control c : controls) 759 { 760 buffer.setLength(0); 761 buffer.append(c.getOID()); 762 buffer.append(':'); 763 buffer.append(c.isCritical()); 764 765 if (c.hasValue()) 766 { 767 final byte[] valueBytes = c.getValue().getValue(); 768 if (StaticUtils.isPrintableString(valueBytes)) 769 { 770 buffer.append(':'); 771 buffer.append(c.getValue().stringValue()); 772 } 773 else 774 { 775 buffer.append("::"); 776 Base64.encode(valueBytes, buffer); 777 } 778 } 779 780 valueStrings.add(buffer.toString()); 781 } 782 783 return Collections.unmodifiableList(valueStrings); 784 } 785 786 787 788 /** 789 * {@inheritDoc} 790 */ 791 @Override() 792 protected boolean hasDefaultValue() 793 { 794 return ((defaultValues != null) && (! defaultValues.isEmpty())); 795 } 796 797 798 799 /** 800 * {@inheritDoc} 801 */ 802 @Override() 803 @NotNull() 804 public String getDataTypeName() 805 { 806 return INFO_CONTROL_TYPE_NAME.get(); 807 } 808 809 810 811 /** 812 * {@inheritDoc} 813 */ 814 @Override() 815 @NotNull() 816 public String getValueConstraints() 817 { 818 return INFO_CONTROL_CONSTRAINTS.get(); 819 } 820 821 822 823 /** 824 * {@inheritDoc} 825 */ 826 @Override() 827 protected void reset() 828 { 829 super.reset(); 830 values.clear(); 831 } 832 833 834 835 /** 836 * {@inheritDoc} 837 */ 838 @Override() 839 @NotNull() 840 public ControlArgument getCleanCopy() 841 { 842 return new ControlArgument(this); 843 } 844 845 846 847 /** 848 * {@inheritDoc} 849 */ 850 @Override() 851 protected void addToCommandLine(@NotNull final List<String> argStrings) 852 { 853 final StringBuilder buffer = new StringBuilder(); 854 for (final Control c : values) 855 { 856 argStrings.add(getIdentifierString()); 857 858 if (isSensitive()) 859 { 860 argStrings.add("***REDACTED***"); 861 continue; 862 } 863 864 buffer.setLength(0); 865 buffer.append(c.getOID()); 866 buffer.append(':'); 867 buffer.append(c.isCritical()); 868 869 if (c.hasValue()) 870 { 871 final byte[] valueBytes = c.getValue().getValue(); 872 if (StaticUtils.isPrintableString(valueBytes)) 873 { 874 buffer.append(':'); 875 buffer.append(c.getValue().stringValue()); 876 } 877 else 878 { 879 buffer.append("::"); 880 Base64.encode(valueBytes, buffer); 881 } 882 } 883 884 argStrings.add(buffer.toString()); 885 } 886 } 887 888 889 890 /** 891 * {@inheritDoc} 892 */ 893 @Override() 894 public void toString(@NotNull final StringBuilder buffer) 895 { 896 buffer.append("ControlArgument("); 897 appendBasicToStringInfo(buffer); 898 899 if ((defaultValues != null) && (! defaultValues.isEmpty())) 900 { 901 if (defaultValues.size() == 1) 902 { 903 buffer.append(", defaultValue='"); 904 buffer.append(defaultValues.get(0).toString()); 905 } 906 else 907 { 908 buffer.append(", defaultValues={"); 909 910 final Iterator<Control> iterator = defaultValues.iterator(); 911 while (iterator.hasNext()) 912 { 913 buffer.append('\''); 914 buffer.append(iterator.next().toString()); 915 buffer.append('\''); 916 917 if (iterator.hasNext()) 918 { 919 buffer.append(", "); 920 } 921 } 922 923 buffer.append('}'); 924 } 925 } 926 927 buffer.append(')'); 928 } 929}