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