001/* 002 * Copyright 2008-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 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.util.Mutable; 031import com.unboundid.util.ThreadSafety; 032import com.unboundid.util.ThreadSafetyLevel; 033 034import static com.unboundid.util.args.ArgsMessages.*; 035 036 037 038/** 039 * This class defines an argument that is intended to hold one or more integer 040 * values. Integer arguments must take values. By default, any value will be 041 * allowed, but it is possible to restrict the set of values to a given range 042 * using upper and lower bounds. 043 */ 044@Mutable() 045@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 046public final class IntegerArgument 047 extends Argument 048{ 049 /** 050 * The serial version UID for this serializable class. 051 */ 052 private static final long serialVersionUID = 3364985217337213643L; 053 054 055 056 // The set of values assigned to this argument. 057 private final ArrayList<Integer> values; 058 059 // The lower bound for this argument. 060 private final int lowerBound; 061 062 // The upper bound for this argument. 063 private final int upperBound; 064 065 // The argument value validators that have been registered for this argument. 066 private final List<ArgumentValueValidator> validators; 067 068 // The list of default values that will be used if no values were provided. 069 private final List<Integer> defaultValues; 070 071 072 073 /** 074 * Creates a new integer argument with the provided information. It will not 075 * be required, will permit at most one occurrence, will use a default 076 * placeholder, will not have a default value, and will not impose any 077 * restrictions on the range of values that may be assigned to this argument. 078 * 079 * @param shortIdentifier The short identifier for this argument. It may 080 * not be {@code null} if the long identifier is 081 * {@code null}. 082 * @param longIdentifier The long identifier for this argument. It may 083 * not be {@code null} if the short identifier is 084 * {@code null}. 085 * @param description A human-readable description for this argument. 086 * It must not be {@code null}. 087 * 088 * @throws ArgumentException If there is a problem with the definition of 089 * this argument. 090 */ 091 public IntegerArgument(final Character shortIdentifier, 092 final String longIdentifier, final String description) 093 throws ArgumentException 094 { 095 this(shortIdentifier, longIdentifier, false, 1, null, description); 096 } 097 098 099 100 /** 101 * Creates a new integer argument with the provided information. There will 102 * not be any default values, nor will there be any restriction on values that 103 * may be assigned to this argument. 104 * 105 * @param shortIdentifier The short identifier for this argument. It may 106 * not be {@code null} if the long identifier is 107 * {@code null}. 108 * @param longIdentifier The long identifier for this argument. It may 109 * not be {@code null} if the short identifier is 110 * {@code null}. 111 * @param isRequired Indicates whether this argument is required to 112 * be provided. 113 * @param maxOccurrences The maximum number of times this argument may be 114 * provided on the command line. A value less than 115 * or equal to zero indicates that it may be present 116 * any number of times. 117 * @param valuePlaceholder A placeholder to display in usage information to 118 * indicate that a value must be provided. It may 119 * be {@code null} if a default placeholder should 120 * be used. 121 * @param description A human-readable description for this argument. 122 * It must not be {@code null}. 123 * 124 * @throws ArgumentException If there is a problem with the definition of 125 * this argument. 126 */ 127 public IntegerArgument(final Character shortIdentifier, 128 final String longIdentifier, final boolean isRequired, 129 final int maxOccurrences, 130 final String valuePlaceholder, 131 final String description) 132 throws ArgumentException 133 { 134 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 135 valuePlaceholder, description, Integer.MIN_VALUE, Integer.MAX_VALUE, 136 (List<Integer>) null); 137 } 138 139 140 141 /** 142 * Creates a new integer argument with the provided information. There will 143 * not be any default values, but the range of values that will be allowed may 144 * be restricted. 145 * 146 * @param shortIdentifier The short identifier for this argument. It may 147 * not be {@code null} if the long identifier is 148 * {@code null}. 149 * @param longIdentifier The long identifier for this argument. It may 150 * not be {@code null} if the short identifier is 151 * {@code null}. 152 * @param isRequired Indicates whether this argument is required to 153 * be provided. 154 * @param maxOccurrences The maximum number of times this argument may be 155 * provided on the command line. A value less than 156 * or equal to zero indicates that it may be present 157 * any number of times. 158 * @param valuePlaceholder A placeholder to display in usage information to 159 * indicate that a value must be provided. It may 160 * be {@code null} if a default placeholder should 161 * be used. 162 * @param description A human-readable description for this argument. 163 * It must not be {@code null}. 164 * @param lowerBound The smallest value that this argument is allowed 165 * to have. It should be {@code Integer.MIN_VALUE} 166 * if there should be no lower bound. 167 * @param upperBound The largest value that this argument is allowed 168 * to have. It should be {@code Integer.MAX_VALUE} 169 * if there should be no upper bound. 170 * 171 * @throws ArgumentException If there is a problem with the definition of 172 * this argument. 173 */ 174 public IntegerArgument(final Character shortIdentifier, 175 final String longIdentifier, final boolean isRequired, 176 final int maxOccurrences, 177 final String valuePlaceholder, 178 final String description, 179 final int lowerBound, final int upperBound) 180 throws ArgumentException 181 { 182 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 183 valuePlaceholder, description, lowerBound, upperBound, 184 (List<Integer>) null); 185 } 186 187 188 189 /** 190 * Creates a new integer argument with the provided information. There will 191 * not be any restriction on values that may be assigned to this argument. 192 * 193 * @param shortIdentifier The short identifier for this argument. It may 194 * not be {@code null} if the long identifier is 195 * {@code null}. 196 * @param longIdentifier The long identifier for this argument. It may 197 * not be {@code null} if the short identifier is 198 * {@code null}. 199 * @param isRequired Indicates whether this argument is required to 200 * be provided. 201 * @param maxOccurrences The maximum number of times this argument may be 202 * provided on the command line. A value less than 203 * or equal to zero indicates that it may be present 204 * any number of times. 205 * @param valuePlaceholder A placeholder to display in usage information to 206 * indicate that a value must be provided. It may 207 * be {@code null} if a default placeholder should 208 * be used. 209 * @param description A human-readable description for this argument. 210 * It must not be {@code null}. 211 * @param defaultValue The default value that will be used for this 212 * argument if no values are provided. It may be 213 * {@code null} if there should not be a default 214 * value. 215 * 216 * @throws ArgumentException If there is a problem with the definition of 217 * this argument. 218 */ 219 public IntegerArgument(final Character shortIdentifier, 220 final String longIdentifier, final boolean isRequired, 221 final int maxOccurrences, 222 final String valuePlaceholder, 223 final String description, 224 final Integer defaultValue) 225 throws ArgumentException 226 { 227 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 228 valuePlaceholder, description, Integer.MIN_VALUE, Integer.MAX_VALUE, 229 ((defaultValue == null) 230 ? null 231 : Collections.singletonList(defaultValue))); 232 } 233 234 235 236 /** 237 * Creates a new integer argument with the provided information. There will 238 * not be any restriction on values that may be assigned to this argument. 239 * 240 * @param shortIdentifier The short identifier for this argument. It may 241 * not be {@code null} if the long identifier is 242 * {@code null}. 243 * @param longIdentifier The long identifier for this argument. It may 244 * not be {@code null} if the short identifier is 245 * {@code null}. 246 * @param isRequired Indicates whether this argument is required to 247 * be provided. 248 * @param maxOccurrences The maximum number of times this argument may be 249 * provided on the command line. A value less than 250 * or equal to zero indicates that it may be present 251 * any number of times. 252 * @param valuePlaceholder A placeholder to display in usage information to 253 * indicate that a value must be provided. It may 254 * be {@code null} if a default placeholder should 255 * be used. 256 * @param description A human-readable description for this argument. 257 * It must not be {@code null}. 258 * @param defaultValues The set of default values that will be used for 259 * this argument if no values are provided. 260 * 261 * @throws ArgumentException If there is a problem with the definition of 262 * this argument. 263 */ 264 public IntegerArgument(final Character shortIdentifier, 265 final String longIdentifier, final boolean isRequired, 266 final int maxOccurrences, 267 final String valuePlaceholder, 268 final String description, 269 final List<Integer> defaultValues) 270 throws ArgumentException 271 { 272 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 273 valuePlaceholder, description, Integer.MIN_VALUE, Integer.MAX_VALUE, 274 defaultValues); 275 } 276 277 278 279 /** 280 * Creates a new integer argument with the provided information. 281 * 282 * @param shortIdentifier The short identifier for this argument. It may 283 * not be {@code null} if the long identifier is 284 * {@code null}. 285 * @param longIdentifier The long identifier for this argument. It may 286 * not be {@code null} if the short identifier is 287 * {@code null}. 288 * @param isRequired Indicates whether this argument is required to 289 * be provided. 290 * @param maxOccurrences The maximum number of times this argument may be 291 * provided on the command line. A value less than 292 * or equal to zero indicates that it may be present 293 * any number of times. 294 * @param valuePlaceholder A placeholder to display in usage information to 295 * indicate that a value must be provided. It may 296 * be {@code null} if a default placeholder should 297 * be used. 298 * @param description A human-readable description for this argument. 299 * It must not be {@code null}. 300 * @param lowerBound The smallest value that this argument is allowed 301 * to have. It should be {@code Integer.MIN_VALUE} 302 * if there should be no lower bound. 303 * @param upperBound The largest value that this argument is allowed 304 * to have. It should be {@code Integer.MAX_VALUE} 305 * if there should be no upper bound. 306 * @param defaultValue The default value that will be used for this 307 * argument if no values are provided. It may be 308 * {@code null} if there should not be a default 309 * value. 310 * 311 * @throws ArgumentException If there is a problem with the definition of 312 * this argument. 313 */ 314 public IntegerArgument(final Character shortIdentifier, 315 final String longIdentifier, final boolean isRequired, 316 final int maxOccurrences, 317 final String valuePlaceholder, 318 final String description, final int lowerBound, 319 final int upperBound, 320 final Integer defaultValue) 321 throws ArgumentException 322 { 323 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 324 valuePlaceholder, description, lowerBound, upperBound, 325 ((defaultValue == null) 326 ? null 327 : Collections.singletonList(defaultValue))); 328 } 329 330 331 332 /** 333 * Creates a new integer argument with the provided information. 334 * 335 * @param shortIdentifier The short identifier for this argument. It may 336 * not be {@code null} if the long identifier is 337 * {@code null}. 338 * @param longIdentifier The long identifier for this argument. It may 339 * not be {@code null} if the short identifier is 340 * {@code null}. 341 * @param isRequired Indicates whether this argument is required to 342 * be provided. 343 * @param maxOccurrences The maximum number of times this argument may be 344 * provided on the command line. A value less than 345 * or equal to zero indicates that it may be present 346 * any number of times. 347 * @param valuePlaceholder A placeholder to display in usage information to 348 * indicate that a value must be provided. It may 349 * be {@code null} if a default placeholder should 350 * be used. 351 * @param description A human-readable description for this argument. 352 * It must not be {@code null}. 353 * @param lowerBound The smallest value that this argument is allowed 354 * to have. It should be {@code Integer.MIN_VALUE} 355 * if there should be no lower bound. 356 * @param upperBound The largest value that this argument is allowed 357 * to have. It should be {@code Integer.MAX_VALUE} 358 * if there should be no upper bound. 359 * @param defaultValues The set of default values that will be used for 360 * this argument if no values are provided. 361 * 362 * @throws ArgumentException If there is a problem with the definition of 363 * this argument. 364 */ 365 public IntegerArgument(final Character shortIdentifier, 366 final String longIdentifier, final boolean isRequired, 367 final int maxOccurrences, 368 final String valuePlaceholder, 369 final String description, final int lowerBound, 370 final int upperBound, 371 final List<Integer> defaultValues) 372 throws ArgumentException 373 { 374 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 375 (valuePlaceholder == null) 376 ? INFO_PLACEHOLDER_VALUE.get() 377 : valuePlaceholder, 378 description); 379 380 this.lowerBound = lowerBound; 381 this.upperBound = upperBound; 382 383 if ((defaultValues == null) || defaultValues.isEmpty()) 384 { 385 this.defaultValues = null; 386 } 387 else 388 { 389 this.defaultValues = Collections.unmodifiableList(defaultValues); 390 } 391 392 values = new ArrayList<>(5); 393 validators = new ArrayList<>(5); 394 } 395 396 397 398 /** 399 * Creates a new integer argument that is a "clean" copy of the provided 400 * source argument. 401 * 402 * @param source The source argument to use for this argument. 403 */ 404 private IntegerArgument(final IntegerArgument source) 405 { 406 super(source); 407 408 lowerBound = source.lowerBound; 409 upperBound = source.upperBound; 410 defaultValues = source.defaultValues; 411 validators = new ArrayList<>(source.validators); 412 values = new ArrayList<>(5); 413 } 414 415 416 417 /** 418 * Retrieves the smallest value that this argument will be allowed to have. 419 * 420 * @return The smallest value that this argument will be allowed to have. 421 */ 422 public int getLowerBound() 423 { 424 return lowerBound; 425 } 426 427 428 429 /** 430 * Retrieves the largest value that this argument will be allowed to have. 431 * 432 * @return The largest value that this argument will be allowed to have. 433 */ 434 public int getUpperBound() 435 { 436 return upperBound; 437 } 438 439 440 441 /** 442 * Retrieves the list of default values for this argument, which will be used 443 * if no values were provided. 444 * 445 * @return The list of default values for this argument, or {@code null} if 446 * there are no default values. 447 */ 448 public List<Integer> getDefaultValues() 449 { 450 return defaultValues; 451 } 452 453 454 455 /** 456 * Updates this argument to ensure that the provided validator will be invoked 457 * for any values provided to this argument. This validator will be invoked 458 * after all other validation has been performed for this argument. 459 * 460 * @param validator The argument value validator to be invoked. It must not 461 * be {@code null}. 462 */ 463 public void addValueValidator(final ArgumentValueValidator validator) 464 { 465 validators.add(validator); 466 } 467 468 469 470 /** 471 * {@inheritDoc} 472 */ 473 @Override() 474 protected void addValue(final String valueString) 475 throws ArgumentException 476 { 477 final int intValue; 478 try 479 { 480 intValue = Integer.parseInt(valueString); 481 } 482 catch (final Exception e) 483 { 484 throw new ArgumentException(ERR_INTEGER_VALUE_NOT_INT.get(valueString, 485 getIdentifierString()), e); 486 } 487 488 if (intValue < lowerBound) 489 { 490 throw new ArgumentException(ERR_INTEGER_VALUE_BELOW_LOWER_BOUND.get( 491 intValue, getIdentifierString(), 492 lowerBound)); 493 } 494 495 if (intValue > upperBound) 496 { 497 throw new ArgumentException(ERR_INTEGER_VALUE_ABOVE_UPPER_BOUND.get( 498 intValue, getIdentifierString(), 499 upperBound)); 500 } 501 502 if (values.size() >= getMaxOccurrences()) 503 { 504 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 505 getIdentifierString())); 506 } 507 508 for (final ArgumentValueValidator v : validators) 509 { 510 v.validateArgumentValue(this, valueString); 511 } 512 513 values.add(intValue); 514 } 515 516 517 518 /** 519 * Retrieves the value for this argument, or the default value if none was 520 * provided. If this argument has multiple values, then the first will be 521 * returned. 522 * 523 * @return The value for this argument, or the default value if none was 524 * provided, or {@code null} if it does not have any values or 525 * default values. 526 */ 527 public Integer getValue() 528 { 529 if (values.isEmpty()) 530 { 531 if ((defaultValues == null) || defaultValues.isEmpty()) 532 { 533 return null; 534 } 535 else 536 { 537 return defaultValues.get(0); 538 } 539 } 540 541 return values.get(0); 542 } 543 544 545 546 /** 547 * Retrieves the set of values for this argument, or the default values if 548 * none were provided. 549 * 550 * @return The set of values for this argument, or the default values if none 551 * were provided. 552 */ 553 public List<Integer> getValues() 554 { 555 if (values.isEmpty() && (defaultValues != null)) 556 { 557 return defaultValues; 558 } 559 560 return Collections.unmodifiableList(values); 561 } 562 563 564 565 /** 566 * {@inheritDoc} 567 */ 568 @Override() 569 public List<String> getValueStringRepresentations(final boolean useDefault) 570 { 571 final List<Integer> intValues; 572 if (values.isEmpty()) 573 { 574 if (useDefault) 575 { 576 intValues = defaultValues; 577 } 578 else 579 { 580 return Collections.emptyList(); 581 } 582 } 583 else 584 { 585 intValues = values; 586 } 587 588 if ((intValues == null) || intValues.isEmpty()) 589 { 590 return Collections.emptyList(); 591 } 592 593 final ArrayList<String> valueStrings = new ArrayList<>(intValues.size()); 594 for (final Integer i : intValues) 595 { 596 valueStrings.add(i.toString()); 597 } 598 return Collections.unmodifiableList(valueStrings); 599 } 600 601 602 603 /** 604 * {@inheritDoc} 605 */ 606 @Override() 607 protected boolean hasDefaultValue() 608 { 609 return ((defaultValues != null) && (! defaultValues.isEmpty())); 610 } 611 612 613 614 /** 615 * {@inheritDoc} 616 */ 617 @Override() 618 public String getDataTypeName() 619 { 620 return INFO_INTEGER_TYPE_NAME.get(); 621 } 622 623 624 625 /** 626 * {@inheritDoc} 627 */ 628 @Override() 629 public String getValueConstraints() 630 { 631 return INFO_INTEGER_CONSTRAINTS_LOWER_AND_UPPER_BOUND.get(lowerBound, 632 upperBound); 633 } 634 635 636 637 /** 638 * {@inheritDoc} 639 */ 640 @Override() 641 protected void reset() 642 { 643 super.reset(); 644 values.clear(); 645 } 646 647 648 649 /** 650 * {@inheritDoc} 651 */ 652 @Override() 653 public IntegerArgument getCleanCopy() 654 { 655 return new IntegerArgument(this); 656 } 657 658 659 660 /** 661 * {@inheritDoc} 662 */ 663 @Override() 664 protected void addToCommandLine(final List<String> argStrings) 665 { 666 if (values != null) 667 { 668 for (final Integer i : values) 669 { 670 argStrings.add(getIdentifierString()); 671 if (isSensitive()) 672 { 673 argStrings.add("***REDACTED"); 674 } 675 else 676 { 677 argStrings.add(i.toString()); 678 } 679 } 680 } 681 } 682 683 684 685 /** 686 * {@inheritDoc} 687 */ 688 @Override() 689 public void toString(final StringBuilder buffer) 690 { 691 buffer.append("IntegerArgument("); 692 appendBasicToStringInfo(buffer); 693 694 buffer.append(", lowerBound="); 695 buffer.append(lowerBound); 696 buffer.append(", upperBound="); 697 buffer.append(upperBound); 698 699 if ((defaultValues != null) && (! defaultValues.isEmpty())) 700 { 701 if (defaultValues.size() == 1) 702 { 703 buffer.append(", defaultValue='"); 704 buffer.append(defaultValues.get(0).toString()); 705 } 706 else 707 { 708 buffer.append(", defaultValues={"); 709 710 final Iterator<Integer> iterator = defaultValues.iterator(); 711 while (iterator.hasNext()) 712 { 713 buffer.append('\''); 714 buffer.append(iterator.next().toString()); 715 buffer.append('\''); 716 717 if (iterator.hasNext()) 718 { 719 buffer.append(", "); 720 } 721 } 722 723 buffer.append('}'); 724 } 725 } 726 727 buffer.append(')'); 728 } 729}