001/* 002 * Copyright 2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015 UnboundID Corp. 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.ArrayList; 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.List; 029 030import com.unboundid.asn1.ASN1OctetString; 031import com.unboundid.ldap.sdk.Control; 032import com.unboundid.util.Base64; 033import com.unboundid.util.Debug; 034import com.unboundid.util.Mutable; 035import com.unboundid.util.StaticUtils; 036import com.unboundid.util.ThreadSafety; 037import com.unboundid.util.ThreadSafetyLevel; 038 039import static com.unboundid.util.args.ArgsMessages.*; 040 041 042 043/** 044 * This class defines an argument that is intended to hold information about one 045 * or more LDAP controls. Values for this argument must be in one of the 046 * following formats: 047 * <UL> 048 * <LI> 049 * oid -- The numeric OID for the control. The control will not be critical 050 * and will not have a value. 051 * </LI> 052 * <LI> 053 * oid:criticality -- The numeric OID followed by a colon and the 054 * criticality. The control will be critical if the criticality value is 055 * any of the following: {@code true}, {@code t}, {@code yes}, {@code y}, 056 * {@code on}, or {@code 1}. The control will be non-critical if the 057 * criticality value is any of the following: {@code false}, {@code f}, 058 * {@code no}, {@code n}, {@code off}, or {@code 0}. No other criticality 059 * values will be accepted. 060 * </LI> 061 * <LI> 062 * oid:criticality:value -- The numeric OID followed by a colon and the 063 * criticality, then a colon and then a string that represents the value for 064 * the control. 065 * </LI> 066 * <LI> 067 * oid:criticality::base64value -- The numeric OID followed by a colon and 068 * the criticality, then two colons and then a string that represents the 069 * base64-encoded value for the control. 070 * </LI> 071 * </UL> 072 */ 073@Mutable() 074@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 075public final class ControlArgument 076 extends Argument 077{ 078 /** 079 * The serial version UID for this serializable class. 080 */ 081 private static final long serialVersionUID = -1889200072476038957L; 082 083 084 085 // The argument value validators that have been registered for this argument. 086 private final List<ArgumentValueValidator> validators; 087 088 // The list of default values for this argument. 089 private final List<Control> defaultValues; 090 091 // The set of values assigned to this argument. 092 private final List<Control> values; 093 094 095 096 /** 097 * Creates a new control argument with the provided information. It will not 098 * have a default value. 099 * 100 * @param shortIdentifier The short identifier for this argument. It may 101 * not be {@code null} if the long identifier is 102 * {@code null}. 103 * @param longIdentifier The long identifier for this argument. It may 104 * not be {@code null} if the short identifier is 105 * {@code null}. 106 * @param isRequired Indicates whether this argument is required to 107 * be provided. 108 * @param maxOccurrences The maximum number of times this argument may be 109 * provided on the command line. A value less than 110 * or equal to zero indicates that it may be present 111 * any number of times. 112 * @param valuePlaceholder A placeholder to display in usage information to 113 * indicate that a value must be provided. It may 114 * be {@code null} to use a default placeholder that 115 * describes the expected syntax for values. 116 * @param description A human-readable description for this argument. 117 * It must not be {@code null}. 118 * 119 * @throws ArgumentException If there is a problem with the definition of 120 * this argument. 121 */ 122 public ControlArgument(final Character shortIdentifier, 123 final String longIdentifier, final boolean isRequired, 124 final int maxOccurrences, 125 final String valuePlaceholder, 126 final String description) 127 throws ArgumentException 128 { 129 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 130 valuePlaceholder, description, (List<Control>) null); 131 } 132 133 134 135 /** 136 * Creates a new control argument with the provided information. 137 * 138 * @param shortIdentifier The short identifier for this argument. It may 139 * not be {@code null} if the long identifier is 140 * {@code null}. 141 * @param longIdentifier The long identifier for this argument. It may 142 * not be {@code null} if the short identifier is 143 * {@code null}. 144 * @param isRequired Indicates whether this argument is required to 145 * be provided. 146 * @param maxOccurrences The maximum number of times this argument may be 147 * provided on the command line. A value less than 148 * or equal to zero indicates that it may be present 149 * any number of times. 150 * @param valuePlaceholder A placeholder to display in usage information to 151 * indicate that a value must be provided. It may 152 * be {@code null} to use a default placeholder that 153 * describes the expected syntax for values. 154 * @param description A human-readable description for this argument. 155 * It must not be {@code null}. 156 * @param defaultValue The default value to use for this argument if no 157 * values were provided. It may be {@code null} if 158 * there should be no default values. 159 * 160 * @throws ArgumentException If there is a problem with the definition of 161 * this argument. 162 */ 163 public ControlArgument(final Character shortIdentifier, 164 final String longIdentifier, final boolean isRequired, 165 final int maxOccurrences, 166 final String valuePlaceholder, 167 final String description, final Control defaultValue) 168 throws ArgumentException 169 { 170 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 171 valuePlaceholder, description, 172 ((defaultValue == null) 173 ? null : 174 Collections.singletonList(defaultValue))); 175 } 176 177 178 179 /** 180 * Creates a new control argument with the provided information. 181 * 182 * @param shortIdentifier The short identifier for this argument. It may 183 * not be {@code null} if the long identifier is 184 * {@code null}. 185 * @param longIdentifier The long identifier for this argument. It may 186 * not be {@code null} if the short identifier is 187 * {@code null}. 188 * @param isRequired Indicates whether this argument is required to 189 * be provided. 190 * @param maxOccurrences The maximum number of times this argument may be 191 * provided on the command line. A value less than 192 * or equal to zero indicates that it may be present 193 * any number of times. 194 * @param valuePlaceholder A placeholder to display in usage information to 195 * indicate that a value must be provided. It may 196 * be {@code null} to use a default placeholder that 197 * describes the expected syntax for values. 198 * @param description A human-readable description for this argument. 199 * It must not be {@code null}. 200 * @param defaultValues The set of default values to use for this 201 * argument if no values were provided. 202 * 203 * @throws ArgumentException If there is a problem with the definition of 204 * this argument. 205 */ 206 public ControlArgument(final Character shortIdentifier, 207 final String longIdentifier, final boolean isRequired, 208 final int maxOccurrences, 209 final String valuePlaceholder, 210 final String description, 211 final List<Control> defaultValues) 212 throws ArgumentException 213 { 214 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 215 (valuePlaceholder == null) 216 ? "{oid}[:{criticality}[:{stringValue}|::{base64Value}]]" 217 : valuePlaceholder, 218 description); 219 220 if ((defaultValues == null) || defaultValues.isEmpty()) 221 { 222 this.defaultValues = null; 223 } 224 else 225 { 226 this.defaultValues = Collections.unmodifiableList(defaultValues); 227 } 228 229 values = new ArrayList<Control>(5); 230 validators = new ArrayList<ArgumentValueValidator>(5); 231 } 232 233 234 235 /** 236 * Creates a new control argument that is a "clean" copy of the provided 237 * source argument. 238 * 239 * @param source The source argument to use for this argument. 240 */ 241 private ControlArgument(final ControlArgument source) 242 { 243 super(source); 244 245 defaultValues = source.defaultValues; 246 validators = new ArrayList<ArgumentValueValidator>(source.validators); 247 values = new ArrayList<Control>(5); 248 } 249 250 251 252 /** 253 * Retrieves the list of default values for this argument, which will be used 254 * if no values were provided. 255 * 256 * @return The list of default values for this argument, or {@code null} if 257 * there are no default values. 258 */ 259 public List<Control> getDefaultValues() 260 { 261 return defaultValues; 262 } 263 264 265 266 /** 267 * Updates this argument to ensure that the provided validator will be invoked 268 * for any values provided to this argument. This validator will be invoked 269 * after all other validation has been performed for this argument. 270 * 271 * @param validator The argument value validator to be invoked. It must not 272 * be {@code null}. 273 */ 274 public void addValueValidator(final ArgumentValueValidator validator) 275 { 276 validators.add(validator); 277 } 278 279 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override() 285 protected void addValue(final String valueString) 286 throws ArgumentException 287 { 288 final String oid; 289 boolean isCritical = false; 290 ASN1OctetString value = null; 291 292 final int firstColonPos = valueString.indexOf(':'); 293 if (firstColonPos < 0) 294 { 295 oid = valueString; 296 } 297 else 298 { 299 oid = valueString.substring(0, firstColonPos); 300 301 final String criticalityStr; 302 final int secondColonPos = valueString.indexOf(':', (firstColonPos+1)); 303 if (secondColonPos < 0) 304 { 305 criticalityStr = valueString.substring(firstColonPos+1); 306 } 307 else 308 { 309 criticalityStr = valueString.substring(firstColonPos+1, secondColonPos); 310 311 final int doubleColonPos = valueString.indexOf("::"); 312 if (doubleColonPos == secondColonPos) 313 { 314 try 315 { 316 value = new ASN1OctetString( 317 Base64.decode(valueString.substring(doubleColonPos+2))); 318 } 319 catch (final Exception e) 320 { 321 Debug.debugException(e); 322 throw new ArgumentException( 323 ERR_CONTROL_ARG_INVALID_BASE64_VALUE.get(valueString, 324 getIdentifierString(), 325 valueString.substring(doubleColonPos+2)), 326 e); 327 } 328 } 329 else 330 { 331 value = new ASN1OctetString(valueString.substring(secondColonPos+1)); 332 } 333 } 334 335 final String lowerCriticalityStr = 336 StaticUtils.toLowerCase(criticalityStr); 337 if (lowerCriticalityStr.equals("true") || 338 lowerCriticalityStr.equals("t") || 339 lowerCriticalityStr.equals("yes") || 340 lowerCriticalityStr.equals("y") || 341 lowerCriticalityStr.equals("on") || 342 lowerCriticalityStr.equals("1")) 343 { 344 isCritical = true; 345 } 346 else if (lowerCriticalityStr.equals("false") || 347 lowerCriticalityStr.equals("f") || 348 lowerCriticalityStr.equals("no") || 349 lowerCriticalityStr.equals("n") || 350 lowerCriticalityStr.equals("off") || 351 lowerCriticalityStr.equals("0")) 352 { 353 isCritical = false; 354 } 355 else 356 { 357 throw new ArgumentException(ERR_CONTROL_ARG_INVALID_CRITICALITY.get( 358 valueString, getIdentifierString(), criticalityStr)); 359 } 360 } 361 362 if (! StaticUtils.isNumericOID(oid)) 363 { 364 throw new ArgumentException(ERR_CONTROL_ARG_INVALID_OID.get( 365 valueString, getIdentifierString(), oid)); 366 } 367 368 if (values.size() >= getMaxOccurrences()) 369 { 370 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 371 getIdentifierString())); 372 } 373 374 for (final ArgumentValueValidator v : validators) 375 { 376 v.validateArgumentValue(this, valueString); 377 } 378 379 values.add(new Control(oid, isCritical, value)); 380 } 381 382 383 384 /** 385 * Retrieves the value for this argument, or the default value if none was 386 * provided. If there are multiple values, then the first will be returned. 387 * 388 * @return The value for this argument, or the default value if none was 389 * provided, or {@code null} if there is no value and no default 390 * value. 391 */ 392 public Control getValue() 393 { 394 if (values.isEmpty()) 395 { 396 if ((defaultValues == null) || defaultValues.isEmpty()) 397 { 398 return null; 399 } 400 else 401 { 402 return defaultValues.get(0); 403 } 404 } 405 else 406 { 407 return values.get(0); 408 } 409 } 410 411 412 413 /** 414 * Retrieves the set of values for this argument, or the default values if 415 * none were provided. 416 * 417 * @return The set of values for this argument, or the default values if none 418 * were provided. 419 */ 420 public List<Control> getValues() 421 { 422 if (values.isEmpty() && (defaultValues != null)) 423 { 424 return defaultValues; 425 } 426 427 return Collections.unmodifiableList(values); 428 } 429 430 431 432 /** 433 * {@inheritDoc} 434 */ 435 @Override() 436 protected boolean hasDefaultValue() 437 { 438 return ((defaultValues != null) && (! defaultValues.isEmpty())); 439 } 440 441 442 443 /** 444 * {@inheritDoc} 445 */ 446 @Override() 447 public String getDataTypeName() 448 { 449 return INFO_CONTROL_TYPE_NAME.get(); 450 } 451 452 453 454 /** 455 * {@inheritDoc} 456 */ 457 @Override() 458 public String getValueConstraints() 459 { 460 return INFO_CONTROL_CONSTRAINTS.get(); 461 } 462 463 464 465 /** 466 * {@inheritDoc} 467 */ 468 @Override() 469 public ControlArgument getCleanCopy() 470 { 471 return new ControlArgument(this); 472 } 473 474 475 476 /** 477 * {@inheritDoc} 478 */ 479 @Override() 480 public void toString(final StringBuilder buffer) 481 { 482 buffer.append("ControlArgument("); 483 appendBasicToStringInfo(buffer); 484 485 if ((defaultValues != null) && (! defaultValues.isEmpty())) 486 { 487 if (defaultValues.size() == 1) 488 { 489 buffer.append(", defaultValue='"); 490 buffer.append(defaultValues.get(0).toString()); 491 } 492 else 493 { 494 buffer.append(", defaultValues={"); 495 496 final Iterator<Control> iterator = defaultValues.iterator(); 497 while (iterator.hasNext()) 498 { 499 buffer.append('\''); 500 buffer.append(iterator.next().toString()); 501 buffer.append('\''); 502 503 if (iterator.hasNext()) 504 { 505 buffer.append(", "); 506 } 507 } 508 509 buffer.append('}'); 510 } 511 } 512 513 buffer.append(')'); 514 } 515}