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