001/* 002 * Copyright 2008-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 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.Arrays; 027import java.util.Collections; 028import java.util.Iterator; 029import java.util.List; 030 031import com.unboundid.ldap.sdk.Filter; 032import com.unboundid.ldap.sdk.LDAPException; 033import com.unboundid.util.Mutable; 034import com.unboundid.util.ThreadSafety; 035import com.unboundid.util.ThreadSafetyLevel; 036 037import static com.unboundid.util.Debug.*; 038import static com.unboundid.util.args.ArgsMessages.*; 039 040 041 042/** 043 * This class defines an argument that is intended to hold one or more 044 * search filter values. Filter arguments must take values, and those values 045 * must be able to be parsed as LDAP search filters. 046 */ 047@Mutable() 048@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 049public final class FilterArgument 050 extends Argument 051{ 052 /** 053 * The serial version UID for this serializable class. 054 */ 055 private static final long serialVersionUID = -1889200072476038957L; 056 057 058 059 // The set of values assigned to this argument. 060 private final ArrayList<Filter> values; 061 062 // The argument value validators that have been registered for this argument. 063 private final List<ArgumentValueValidator> validators; 064 065 // The list of default values for this argument. 066 private final List<Filter> defaultValues; 067 068 069 070 /** 071 * Creates a new filter argument with the provided information. It will not 072 * be required, will permit at most one occurrence, will use a default 073 * placeholder, and will not have a default value. 074 * 075 * @param shortIdentifier The short identifier for this argument. It may 076 * not be {@code null} if the long identifier is 077 * {@code null}. 078 * @param longIdentifier The long identifier for this argument. It may 079 * not be {@code null} if the short identifier is 080 * {@code null}. 081 * @param description A human-readable description for this argument. 082 * It must not be {@code null}. 083 * 084 * @throws ArgumentException If there is a problem with the definition of 085 * this argument. 086 */ 087 public FilterArgument(final Character shortIdentifier, 088 final String longIdentifier, final String description) 089 throws ArgumentException 090 { 091 this(shortIdentifier, longIdentifier, false, 1, null, description); 092 } 093 094 095 096 /** 097 * Creates a new filter 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} if a default placeholder should 115 * be used. 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 FilterArgument(final Character shortIdentifier, 123 final String longIdentifier, final boolean isRequired, 124 final int maxOccurrences, final String valuePlaceholder, 125 final String description) 126 throws ArgumentException 127 { 128 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 129 valuePlaceholder, description, (List<Filter>) null); 130 } 131 132 133 134 /** 135 * Creates a new filter argument with the provided information. 136 * 137 * @param shortIdentifier The short identifier for this argument. It may 138 * not be {@code null} if the long identifier is 139 * {@code null}. 140 * @param longIdentifier The long identifier for this argument. It may 141 * not be {@code null} if the short identifier is 142 * {@code null}. 143 * @param isRequired Indicates whether this argument is required to 144 * be provided. 145 * @param maxOccurrences The maximum number of times this argument may be 146 * provided on the command line. A value less than 147 * or equal to zero indicates that it may be present 148 * any number of times. 149 * @param valuePlaceholder A placeholder to display in usage information to 150 * indicate that a value must be provided. It may 151 * be {@code null} if a default placeholder should 152 * be used. 153 * @param description A human-readable description for this argument. 154 * It must not be {@code null}. 155 * @param defaultValue The default value to use for this argument if no 156 * values were provided. It may be {@code null} if 157 * there should be no default values. 158 * 159 * @throws ArgumentException If there is a problem with the definition of 160 * this argument. 161 */ 162 public FilterArgument(final Character shortIdentifier, 163 final String longIdentifier, final boolean isRequired, 164 final int maxOccurrences, final String valuePlaceholder, 165 final String description, 166 final Filter defaultValue) 167 throws ArgumentException 168 { 169 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 170 valuePlaceholder, description, 171 ((defaultValue == null) ? null : Arrays.asList(defaultValue))); 172 } 173 174 175 176 /** 177 * Creates a new filter argument with the provided information. 178 * 179 * @param shortIdentifier The short identifier for this argument. It may 180 * not be {@code null} if the long identifier is 181 * {@code null}. 182 * @param longIdentifier The long identifier for this argument. It may 183 * not be {@code null} if the short identifier is 184 * {@code null}. 185 * @param isRequired Indicates whether this argument is required to 186 * be provided. 187 * @param maxOccurrences The maximum number of times this argument may be 188 * provided on the command line. A value less than 189 * or equal to zero indicates that it may be present 190 * any number of times. 191 * @param valuePlaceholder A placeholder to display in usage information to 192 * indicate that a value must be provided. It may 193 * be {@code null} if a default placeholder should 194 * be used. 195 * @param description A human-readable description for this argument. 196 * It must not be {@code null}. 197 * @param defaultValues The set of default values to use for this 198 * argument if no values were provided. 199 * 200 * @throws ArgumentException If there is a problem with the definition of 201 * this argument. 202 */ 203 public FilterArgument(final Character shortIdentifier, 204 final String longIdentifier, final boolean isRequired, 205 final int maxOccurrences, final String valuePlaceholder, 206 final String description, 207 final List<Filter> defaultValues) 208 throws ArgumentException 209 { 210 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 211 (valuePlaceholder == null) 212 ? INFO_PLACEHOLDER_FILTER.get() 213 : valuePlaceholder, 214 description); 215 216 if ((defaultValues == null) || defaultValues.isEmpty()) 217 { 218 this.defaultValues = null; 219 } 220 else 221 { 222 this.defaultValues = Collections.unmodifiableList(defaultValues); 223 } 224 225 values = new ArrayList<Filter>(5); 226 validators = new ArrayList<ArgumentValueValidator>(5); 227 } 228 229 230 231 /** 232 * Creates a new filter argument that is a "clean" copy of the provided source 233 * argument. 234 * 235 * @param source The source argument to use for this argument. 236 */ 237 private FilterArgument(final FilterArgument source) 238 { 239 super(source); 240 241 defaultValues = source.defaultValues; 242 validators = new ArrayList<ArgumentValueValidator>(source.validators); 243 values = new ArrayList<Filter>(5); 244 } 245 246 247 248 /** 249 * Retrieves the list of default values for this argument, which will be used 250 * if no values were provided. 251 * 252 * @return The list of default values for this argument, or {@code null} if 253 * there are no default values. 254 */ 255 public List<Filter> getDefaultValues() 256 { 257 return defaultValues; 258 } 259 260 261 262 /** 263 * Updates this argument to ensure that the provided validator will be invoked 264 * for any values provided to this argument. This validator will be invoked 265 * after all other validation has been performed for this argument. 266 * 267 * @param validator The argument value validator to be invoked. It must not 268 * be {@code null}. 269 */ 270 public void addValueValidator(final ArgumentValueValidator validator) 271 { 272 validators.add(validator); 273 } 274 275 276 277 /** 278 * {@inheritDoc} 279 */ 280 @Override() 281 protected void addValue(final String valueString) 282 throws ArgumentException 283 { 284 final Filter filter; 285 try 286 { 287 filter = Filter.create(valueString); 288 } 289 catch (LDAPException le) 290 { 291 debugException(le); 292 throw new ArgumentException(ERR_FILTER_VALUE_NOT_FILTER.get(valueString, 293 getIdentifierString(), le.getMessage()), 294 le); 295 } 296 297 if (values.size() >= getMaxOccurrences()) 298 { 299 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 300 getIdentifierString())); 301 } 302 303 for (final ArgumentValueValidator v : validators) 304 { 305 v.validateArgumentValue(this, valueString); 306 } 307 308 values.add(filter); 309 } 310 311 312 313 /** 314 * Retrieves the value for this argument, or the default value if none was 315 * provided. If there are multiple values, then the first will be returned. 316 * 317 * @return The value for this argument, or the default value if none was 318 * provided, or {@code null} if there is no value and no default 319 * value. 320 */ 321 public Filter getValue() 322 { 323 if (values.isEmpty()) 324 { 325 if ((defaultValues == null) || defaultValues.isEmpty()) 326 { 327 return null; 328 } 329 else 330 { 331 return defaultValues.get(0); 332 } 333 } 334 else 335 { 336 return values.get(0); 337 } 338 } 339 340 341 342 /** 343 * Retrieves the set of values for this argument, or the default values if 344 * none were provided. 345 * 346 * @return The set of values for this argument, or the default values if none 347 * were provided. 348 */ 349 public List<Filter> getValues() 350 { 351 if (values.isEmpty() && (defaultValues != null)) 352 { 353 return defaultValues; 354 } 355 356 return Collections.unmodifiableList(values); 357 } 358 359 360 361 /** 362 * {@inheritDoc} 363 */ 364 @Override() 365 public List<String> getValueStringRepresentations(final boolean useDefault) 366 { 367 final List<Filter> filters; 368 if (values.isEmpty()) 369 { 370 if (useDefault) 371 { 372 filters = defaultValues; 373 } 374 else 375 { 376 return Collections.emptyList(); 377 } 378 } 379 else 380 { 381 filters = values; 382 } 383 384 if ((filters == null) || filters.isEmpty()) 385 { 386 return Collections.emptyList(); 387 } 388 389 final ArrayList<String> valueStrings = 390 new ArrayList<String>(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}