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