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