001/* 002 * Copyright 2008-2023 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2023 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-2023 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.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 * search filter values. Filter arguments must take values, and those values 061 * must be able to be parsed as LDAP search filters. 062 */ 063@Mutable() 064@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 065public final class FilterArgument 066 extends Argument 067{ 068 /** 069 * The serial version UID for this serializable class. 070 */ 071 private static final long serialVersionUID = -1889200072476038957L; 072 073 074 075 // The set of values assigned to this argument. 076 @NotNull private final ArrayList<Filter> 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<Filter> defaultValues; 083 084 085 086 /** 087 * Creates a new filter argument with the provided information. It will not 088 * be 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 FilterArgument(@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 filter argument with the provided information. It will not 115 * have 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 FilterArgument(@Nullable final Character shortIdentifier, 140 @Nullable final String longIdentifier, 141 final boolean isRequired, final int maxOccurrences, 142 @Nullable final String valuePlaceholder, 143 @NotNull final String description) 144 throws ArgumentException 145 { 146 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 147 valuePlaceholder, description, (List<Filter>) null); 148 } 149 150 151 152 /** 153 * Creates a new filter argument with the provided information. 154 * 155 * @param shortIdentifier The short identifier for this argument. It may 156 * not be {@code null} if the long identifier is 157 * {@code null}. 158 * @param longIdentifier The long identifier for this argument. It may 159 * not be {@code null} if the short identifier is 160 * {@code null}. 161 * @param isRequired Indicates whether this argument is required to 162 * be provided. 163 * @param maxOccurrences The maximum number of times this argument may be 164 * provided on the command line. A value less than 165 * or equal to zero indicates that it may be present 166 * any number of times. 167 * @param valuePlaceholder A placeholder to display in usage information to 168 * indicate that a value must be provided. It may 169 * be {@code null} if a default placeholder should 170 * be used. 171 * @param description A human-readable description for this argument. 172 * It must not be {@code null}. 173 * @param defaultValue The default value to use for this argument if no 174 * values were provided. It may be {@code null} if 175 * there should be no default values. 176 * 177 * @throws ArgumentException If there is a problem with the definition of 178 * this argument. 179 */ 180 public FilterArgument(@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 Filter 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 filter 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 FilterArgument(@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<Filter> defaultValues) 230 throws ArgumentException 231 { 232 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 233 (valuePlaceholder == null) 234 ? INFO_PLACEHOLDER_FILTER.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 filter 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 FilterArgument(@NotNull final FilterArgument source) 260 { 261 super(source); 262 263 defaultValues = source.defaultValues; 264 validators = new ArrayList<>(source.validators); 265 values = new ArrayList<>(5); 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<Filter> 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 Filter filter; 308 try 309 { 310 filter = Filter.create(valueString); 311 } 312 catch (final LDAPException le) 313 { 314 Debug.debugException(le); 315 throw new ArgumentException(ERR_FILTER_VALUE_NOT_FILTER.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(filter); 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 Filter 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, or the default values if 368 * none were provided. 369 * 370 * @return The set of values for this argument, or the default values if none 371 * were provided. 372 */ 373 @NotNull() 374 public List<Filter> getValues() 375 { 376 if (values.isEmpty() && (defaultValues != null)) 377 { 378 return defaultValues; 379 } 380 381 return Collections.unmodifiableList(values); 382 } 383 384 385 386 /** 387 * {@inheritDoc} 388 */ 389 @Override() 390 @NotNull() 391 public List<String> getValueStringRepresentations(final boolean useDefault) 392 { 393 final List<Filter> filters; 394 if (values.isEmpty()) 395 { 396 if (useDefault) 397 { 398 filters = defaultValues; 399 } 400 else 401 { 402 return Collections.emptyList(); 403 } 404 } 405 else 406 { 407 filters = values; 408 } 409 410 if ((filters == null) || filters.isEmpty()) 411 { 412 return Collections.emptyList(); 413 } 414 415 final ArrayList<String> valueStrings = new ArrayList<>(filters.size()); 416 for (final Filter f : filters) 417 { 418 valueStrings.add(f.toString()); 419 } 420 return Collections.unmodifiableList(valueStrings); 421 } 422 423 424 425 /** 426 * {@inheritDoc} 427 */ 428 @Override() 429 protected boolean hasDefaultValue() 430 { 431 return ((defaultValues != null) && (! defaultValues.isEmpty())); 432 } 433 434 435 436 /** 437 * {@inheritDoc} 438 */ 439 @Override() 440 @NotNull() 441 public String getDataTypeName() 442 { 443 return INFO_FILTER_TYPE_NAME.get(); 444 } 445 446 447 448 /** 449 * {@inheritDoc} 450 */ 451 @Override() 452 @NotNull() 453 public String getValueConstraints() 454 { 455 return INFO_FILTER_CONSTRAINTS.get(); 456 } 457 458 459 460 /** 461 * {@inheritDoc} 462 */ 463 @Override() 464 protected void reset() 465 { 466 super.reset(); 467 values.clear(); 468 } 469 470 471 472 /** 473 * {@inheritDoc} 474 */ 475 @Override() 476 @NotNull() 477 public FilterArgument getCleanCopy() 478 { 479 return new FilterArgument(this); 480 } 481 482 483 484 /** 485 * {@inheritDoc} 486 */ 487 @Override() 488 protected void addToCommandLine(@NotNull final List<String> argStrings) 489 { 490 for (final Filter f : values) 491 { 492 argStrings.add(getIdentifierString()); 493 if (isSensitive()) 494 { 495 argStrings.add("***REDACTED***"); 496 } 497 else 498 { 499 argStrings.add(f.toString()); 500 } 501 } 502 } 503 504 505 506 /** 507 * {@inheritDoc} 508 */ 509 @Override() 510 public void toString(@NotNull final StringBuilder buffer) 511 { 512 buffer.append("FilterArgument("); 513 appendBasicToStringInfo(buffer); 514 515 if ((defaultValues != null) && (! defaultValues.isEmpty())) 516 { 517 if (defaultValues.size() == 1) 518 { 519 buffer.append(", defaultValue='"); 520 buffer.append(defaultValues.get(0).toString()); 521 } 522 else 523 { 524 buffer.append(", defaultValues={"); 525 526 final Iterator<Filter> iterator = defaultValues.iterator(); 527 while (iterator.hasNext()) 528 { 529 buffer.append('\''); 530 buffer.append(iterator.next().toString()); 531 buffer.append('\''); 532 533 if (iterator.hasNext()) 534 { 535 buffer.append(", "); 536 } 537 } 538 539 buffer.append('}'); 540 } 541 } 542 543 buffer.append(')'); 544 } 545}