001/* 002 * Copyright 2010-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2010-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.List; 029import java.util.concurrent.TimeUnit; 030 031import com.unboundid.util.Debug; 032import com.unboundid.util.LDAPSDKUsageException; 033import com.unboundid.util.Mutable; 034import com.unboundid.util.StaticUtils; 035import com.unboundid.util.ThreadSafety; 036import com.unboundid.util.ThreadSafetyLevel; 037 038import static com.unboundid.util.args.ArgsMessages.*; 039 040 041 042/** 043 * Creates a new argument that is intended to represent a duration. Duration 044 * values contain an integer portion and a unit portion which represents the 045 * time unit. The unit must be one of the following: 046 * <UL> 047 * <LI>Nanoseconds -- ns, nano, nanos, nanosecond, nanoseconds</LI> 048 * <LI>Microseconds -- us, micro, micros, microsecond, microseconds</LI> 049 * <LI>Milliseconds -- ms, milli, millis, millisecond, milliseconds</LI> 050 * <LI>Seconds -- s, sec, secs, second, seconds</LI> 051 * <LI>Minutes -- m, min, mins, minute, minutes</LI> 052 * <LI>Hours -- h, hr, hrs, hour, hours</LI> 053 * <LI>Days -- d, day, days</LI> 054 * </UL> 055 * 056 * There may be zero or more spaces between the integer portion and the unit 057 * portion. However, if spaces are used in the command-line argument, then the 058 * value must be enquoted or the spaces must be escaped so that the duration 059 * is not seen as multiple arguments. 060 */ 061@Mutable() 062@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 063public final class DurationArgument 064 extends Argument 065{ 066 /** 067 * The serial version UID for this serializable class. 068 */ 069 private static final long serialVersionUID = -8824262632728709264L; 070 071 072 073 // The argument value validators that have been registered for this argument. 074 private final List<ArgumentValueValidator> validators; 075 076 // The default value for this argument, in nanoseconds. 077 private final Long defaultValueNanos; 078 079 // The maximum allowed value for this argument, in nanoseconds. 080 private final long maxValueNanos; 081 082 // The minimum allowed value for this argument, in nanoseconds. 083 private final long minValueNanos; 084 085 // The provided value for this argument, in nanoseconds. 086 private Long valueNanos; 087 088 // The string representation of the lower bound, using the user-supplied 089 // value. 090 private final String lowerBoundStr; 091 092 // The string representation of the upper bound, using the user-supplied 093 // value. 094 private final String upperBoundStr; 095 096 097 098 /** 099 * Creates a new duration argument that will not be required, will use a 100 * default placeholder, and will have no default value and no bounds on the 101 * set of allowed values. 102 * 103 * @param shortIdentifier The short identifier for this argument. It may 104 * not be {@code null} if the long identifier is 105 * {@code null}. 106 * @param longIdentifier The long identifier for this argument. It may 107 * not be {@code null} if the short identifier is 108 * {@code null}. 109 * @param description A human-readable description for this argument. 110 * It must not be {@code null}. 111 * 112 * @throws ArgumentException If there is a problem with the definition of 113 * this argument. 114 */ 115 public DurationArgument(final Character shortIdentifier, 116 final String longIdentifier, final String description) 117 throws ArgumentException 118 { 119 this(shortIdentifier, longIdentifier, false, null, description); 120 } 121 122 123 124 /** 125 * Creates a new duration argument with no default value and no bounds on the 126 * set of allowed values. 127 * 128 * @param shortIdentifier The short identifier for this argument. It may 129 * not be {@code null} if the long identifier is 130 * {@code null}. 131 * @param longIdentifier The long identifier for this argument. It may 132 * not be {@code null} if the short identifier is 133 * {@code null}. 134 * @param isRequired Indicates whether this argument is required to 135 * be provided. 136 * @param valuePlaceholder A placeholder to display in usage information to 137 * indicate that a value must be provided. It may 138 * be {@code null} if a default placeholder should 139 * be used. 140 * @param description A human-readable description for this argument. 141 * It must not be {@code null}. 142 * 143 * @throws ArgumentException If there is a problem with the definition of 144 * this argument. 145 */ 146 public DurationArgument(final Character shortIdentifier, 147 final String longIdentifier, final boolean isRequired, 148 final String valuePlaceholder, 149 final String description) 150 throws ArgumentException 151 { 152 this(shortIdentifier, longIdentifier, isRequired, valuePlaceholder, 153 description, null, null, null, null, null, null); 154 } 155 156 157 158 /** 159 * Creates a new duration argument with the provided information. 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 valuePlaceholder A placeholder to display in usage information to 170 * indicate that a value must be provided. It may 171 * be {@code null} if a default placeholder should 172 * be used. 173 * @param description A human-readable description for this argument. 174 * It must not be {@code null}. 175 * @param defaultValue The default value that will be used for this 176 * argument if none is provided. It may be 177 * {@code null} if there should not be a default 178 * value. 179 * @param defaultValueUnit The time unit for the default value. It may be 180 * {@code null} only if the default value is also 181 * {@code null}. 182 * @param lowerBound The value for the minimum duration that may be 183 * represented using this argument, in conjunction 184 * with the {@code lowerBoundUnit} parameter to 185 * specify the unit for this value. If this is 186 * {@code null}, then a lower bound of 0 nanoseconds 187 * will be used. 188 * @param lowerBoundUnit The time unit for the lower bound value. It may 189 * be {@code null} only if the lower bound is also 190 * {@code null}. 191 * @param upperBound The value for the maximum duration that may be 192 * represented using this argument, in conjunction 193 * with the {@code upperBoundUnit} parameter to 194 * specify the unit for this value. If this is 195 * {@code null}, then an upper bound of 196 * {@code Long.MAX_VALUE} nanoseconds will be used. 197 * @param upperBoundUnit The time unit for the upper bound value. It may 198 * be {@code null} only if the upper bound is also 199 * {@code null}. 200 * 201 * @throws ArgumentException If there is a problem with the definition of 202 * this argument. 203 */ 204 public DurationArgument(final Character shortIdentifier, 205 final String longIdentifier, final boolean isRequired, 206 final String valuePlaceholder, 207 final String description, final Long defaultValue, 208 final TimeUnit defaultValueUnit, 209 final Long lowerBound, final TimeUnit lowerBoundUnit, 210 final Long upperBound, final TimeUnit upperBoundUnit) 211 throws ArgumentException 212 { 213 super(shortIdentifier, longIdentifier, isRequired, 1, 214 (valuePlaceholder == null) 215 ? INFO_PLACEHOLDER_DURATION.get() 216 : valuePlaceholder, 217 description); 218 219 if (defaultValue == null) 220 { 221 defaultValueNanos = null; 222 } 223 else 224 { 225 if (defaultValueUnit == null) 226 { 227 throw new ArgumentException(ERR_DURATION_DEFAULT_REQUIRES_UNIT.get( 228 getIdentifierString())); 229 } 230 231 defaultValueNanos = defaultValueUnit.toNanos(defaultValue); 232 } 233 234 if (lowerBound == null) 235 { 236 minValueNanos = 0L; 237 lowerBoundStr = "0ns"; 238 } 239 else 240 { 241 if (lowerBoundUnit == null) 242 { 243 throw new ArgumentException(ERR_DURATION_LOWER_REQUIRES_UNIT.get( 244 getIdentifierString())); 245 } 246 247 minValueNanos = lowerBoundUnit.toNanos(lowerBound); 248 final String lowerBoundUnitName = lowerBoundUnit.name(); 249 if (lowerBoundUnitName.equals("NANOSECONDS")) 250 { 251 lowerBoundStr = minValueNanos + "ns"; 252 } 253 else if (lowerBoundUnitName.equals("MICROSECONDS")) 254 { 255 lowerBoundStr = lowerBound + "us"; 256 } 257 else if (lowerBoundUnitName.equals("MILLISECONDS")) 258 { 259 lowerBoundStr = lowerBound + "ms"; 260 } 261 else if (lowerBoundUnitName.equals("SECONDS")) 262 { 263 lowerBoundStr = lowerBound + "s"; 264 } 265 else if (lowerBoundUnitName.equals("MINUTES")) 266 { 267 lowerBoundStr = lowerBound + "m"; 268 } 269 else if (lowerBoundUnitName.equals("HOURS")) 270 { 271 lowerBoundStr = lowerBound + "h"; 272 } 273 else if (lowerBoundUnitName.equals("DAYS")) 274 { 275 lowerBoundStr = lowerBound + "d"; 276 } 277 else 278 { 279 throw new LDAPSDKUsageException( 280 ERR_DURATION_UNSUPPORTED_LOWER_BOUND_UNIT.get(lowerBoundUnitName)); 281 } 282 } 283 284 if (upperBound == null) 285 { 286 maxValueNanos = Long.MAX_VALUE; 287 upperBoundStr = Long.MAX_VALUE + "ns"; 288 } 289 else 290 { 291 if (upperBoundUnit == null) 292 { 293 throw new ArgumentException(ERR_DURATION_UPPER_REQUIRES_UNIT.get( 294 getIdentifierString())); 295 } 296 297 maxValueNanos = upperBoundUnit.toNanos(upperBound); 298 final String upperBoundUnitName = upperBoundUnit.name(); 299 if (upperBoundUnitName.equals("NANOSECONDS")) 300 { 301 upperBoundStr = minValueNanos + "ns"; 302 } 303 else if (upperBoundUnitName.equals("MICROSECONDS")) 304 { 305 upperBoundStr = upperBound + "us"; 306 } 307 else if (upperBoundUnitName.equals("MILLISECONDS")) 308 { 309 upperBoundStr = upperBound + "ms"; 310 } 311 else if (upperBoundUnitName.equals("SECONDS")) 312 { 313 upperBoundStr = upperBound + "s"; 314 } 315 else if (upperBoundUnitName.equals("MINUTES")) 316 { 317 upperBoundStr = upperBound + "m"; 318 } 319 else if (upperBoundUnitName.equals("HOURS")) 320 { 321 upperBoundStr = upperBound + "h"; 322 } 323 else if (upperBoundUnitName.equals("DAYS")) 324 { 325 upperBoundStr = upperBound + "d"; 326 } 327 else 328 { 329 throw new LDAPSDKUsageException( 330 ERR_DURATION_UNSUPPORTED_UPPER_BOUND_UNIT.get(upperBoundUnitName)); 331 } 332 } 333 334 if (minValueNanos > maxValueNanos) 335 { 336 throw new ArgumentException(ERR_DURATION_LOWER_GT_UPPER.get( 337 getIdentifierString(), lowerBoundStr, upperBoundStr)); 338 } 339 340 valueNanos = null; 341 validators = new ArrayList<ArgumentValueValidator>(5); 342 } 343 344 345 346 /** 347 * Creates a new duration argument that is a "clean" copy of the provided 348 * source argument. 349 * 350 * @param source The source argument to use for this argument. 351 */ 352 private DurationArgument(final DurationArgument source) 353 { 354 super(source); 355 356 defaultValueNanos = source.defaultValueNanos; 357 maxValueNanos = source.maxValueNanos; 358 minValueNanos = source.minValueNanos; 359 lowerBoundStr = source.lowerBoundStr; 360 upperBoundStr = source.upperBoundStr; 361 validators = 362 new ArrayList<ArgumentValueValidator>(source.validators); 363 valueNanos = null; 364 } 365 366 367 368 /** 369 * Retrieves the lower bound for this argument using the specified time unit. 370 * 371 * @param unit The time unit in which the lower bound value may be 372 * expressed. 373 * 374 * @return The lower bound for this argument using the specified time unit. 375 */ 376 public long getLowerBound(final TimeUnit unit) 377 { 378 return unit.convert(minValueNanos, TimeUnit.NANOSECONDS); 379 } 380 381 382 383 /** 384 * Retrieves the upper bound for this argument using the specified time unit. 385 * 386 * @param unit The time unit in which the upper bound value may be 387 * expressed. 388 * 389 * @return The upper bound for this argument using the specified time unit. 390 */ 391 public long getUpperBound(final TimeUnit unit) 392 { 393 return unit.convert(maxValueNanos, TimeUnit.NANOSECONDS); 394 } 395 396 397 398 /** 399 * {@inheritDoc} 400 */ 401 @Override() 402 public List<String> getValueStringRepresentations(final boolean useDefault) 403 { 404 final long v; 405 if (valueNanos != null) 406 { 407 v = valueNanos; 408 } 409 else if (useDefault && (defaultValueNanos != null)) 410 { 411 v = defaultValueNanos; 412 } 413 else 414 { 415 return Collections.emptyList(); 416 } 417 418 return Collections.unmodifiableList(Arrays.asList(nanosToDuration(v))); 419 } 420 421 422 423 /** 424 * {@inheritDoc} 425 */ 426 @Override() 427 protected boolean hasDefaultValue() 428 { 429 return (defaultValueNanos != null); 430 } 431 432 433 434 /** 435 * Retrieves the default value for this argument using the specified time 436 * unit, if defined. 437 * 438 * @param unit The time unit in which the default value should be expressed. 439 * 440 * @return The default value for this argument using the specified time unit, 441 * or {@code null} if none is defined. 442 */ 443 public Long getDefaultValue(final TimeUnit unit) 444 { 445 if (defaultValueNanos == null) 446 { 447 return null; 448 } 449 450 return unit.convert(defaultValueNanos, TimeUnit.NANOSECONDS); 451 } 452 453 454 455 /** 456 * Retrieves the value for this argument using the specified time unit, if one 457 * was provided. 458 * 459 * @param unit The time unit in which to express the value for this 460 * argument. 461 * 462 * @return The value for this argument using the specified time unit. If no 463 * value was provided but a default value was defined, then the 464 * default value will be returned. If no value was provided and no 465 * default value was defined, then {@code null} will be returned. 466 */ 467 public Long getValue(final TimeUnit unit) 468 { 469 if (valueNanos == null) 470 { 471 if (defaultValueNanos == null) 472 { 473 return null; 474 } 475 476 return unit.convert(defaultValueNanos, TimeUnit.NANOSECONDS); 477 } 478 else 479 { 480 return unit.convert(valueNanos, TimeUnit.NANOSECONDS); 481 } 482 } 483 484 485 486 /** 487 * Updates this argument to ensure that the provided validator will be invoked 488 * for any values provided to this argument. This validator will be invoked 489 * after all other validation has been performed for this argument. 490 * 491 * @param validator The argument value validator to be invoked. It must not 492 * be {@code null}. 493 */ 494 public void addValueValidator(final ArgumentValueValidator validator) 495 { 496 validators.add(validator); 497 } 498 499 500 501 /** 502 * {@inheritDoc} 503 */ 504 @Override() 505 protected void addValue(final String valueString) 506 throws ArgumentException 507 { 508 if (valueNanos != null) 509 { 510 throw new ArgumentException( 511 ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(getIdentifierString())); 512 } 513 514 final long proposedValueNanos; 515 try 516 { 517 proposedValueNanos = parseDuration(valueString, TimeUnit.NANOSECONDS); 518 } 519 catch (final ArgumentException ae) 520 { 521 Debug.debugException(ae); 522 throw new ArgumentException( 523 ERR_DURATION_MALFORMED_VALUE.get(valueString, getIdentifierString(), 524 ae.getMessage()), 525 ae); 526 } 527 528 if (proposedValueNanos < minValueNanos) 529 { 530 throw new ArgumentException(ERR_DURATION_BELOW_LOWER_BOUND.get( 531 getIdentifierString(), lowerBoundStr)); 532 } 533 else if (proposedValueNanos > maxValueNanos) 534 { 535 throw new ArgumentException(ERR_DURATION_ABOVE_UPPER_BOUND.get( 536 getIdentifierString(), upperBoundStr)); 537 } 538 else 539 { 540 for (final ArgumentValueValidator v : validators) 541 { 542 v.validateArgumentValue(this, valueString); 543 } 544 545 valueNanos = proposedValueNanos; 546 } 547 } 548 549 550 551 /** 552 * Parses the provided string representation of a duration to a corresponding 553 * numeric representation. 554 * 555 * @param durationString The string representation of the duration to be 556 * parsed. 557 * @param timeUnit The time unit to use for the return value. 558 * 559 * @return The parsed duration as a count in the specified time unit. 560 * 561 * @throws ArgumentException If the provided string cannot be parsed as a 562 * valid duration. 563 */ 564 public static long parseDuration(final String durationString, 565 final TimeUnit timeUnit) 566 throws ArgumentException 567 { 568 // The string must not be empty. 569 final String lowerStr = StaticUtils.toLowerCase(durationString); 570 if (lowerStr.length() == 0) 571 { 572 throw new ArgumentException(ERR_DURATION_EMPTY_VALUE.get()); 573 } 574 575 // Find the position of the first non-digit character. 576 boolean digitFound = false; 577 boolean nonDigitFound = false; 578 int nonDigitPos = -1; 579 for (int i=0; i < lowerStr.length(); i++) 580 { 581 final char c = lowerStr.charAt(i); 582 if (Character.isDigit(c)) 583 { 584 digitFound = true; 585 } 586 else 587 { 588 nonDigitFound = true; 589 nonDigitPos = i; 590 if (! digitFound) 591 { 592 throw new ArgumentException(ERR_DURATION_NO_DIGIT.get()); 593 } 594 break; 595 } 596 } 597 598 if (! nonDigitFound) 599 { 600 throw new ArgumentException(ERR_DURATION_NO_UNIT.get()); 601 } 602 603 // Separate the integer portion from the unit. 604 long integerPortion = Long.parseLong(lowerStr.substring(0, nonDigitPos)); 605 final String unitStr = lowerStr.substring(nonDigitPos).trim(); 606 607 // Parse the time unit. 608 final TimeUnit unitFromString; 609 if (unitStr.equals("ns") || 610 unitStr.equals("nano") || 611 unitStr.equals("nanos") || 612 unitStr.equals("nanosecond") || 613 unitStr.equals("nanoseconds")) 614 { 615 unitFromString = TimeUnit.NANOSECONDS; 616 } 617 else if (unitStr.equals("us") || 618 unitStr.equals("micro") || 619 unitStr.equals("micros") || 620 unitStr.equals("microsecond") || 621 unitStr.equals("microseconds")) 622 { 623 unitFromString = TimeUnit.MICROSECONDS; 624 } 625 else if (unitStr.equals("ms") || 626 unitStr.equals("milli") || 627 unitStr.equals("millis") || 628 unitStr.equals("millisecond") || 629 unitStr.equals("milliseconds")) 630 { 631 unitFromString = TimeUnit.MILLISECONDS; 632 } 633 else if (unitStr.equals("s") || 634 unitStr.equals("sec") || 635 unitStr.equals("secs") || 636 unitStr.equals("second") || 637 unitStr.equals("seconds")) 638 { 639 unitFromString = TimeUnit.SECONDS; 640 } 641 else if (unitStr.equals("m") || 642 unitStr.equals("min") || 643 unitStr.equals("mins") || 644 unitStr.equals("minute") || 645 unitStr.equals("minutes")) 646 { 647 integerPortion *= 60L; 648 unitFromString = TimeUnit.SECONDS; 649 } 650 else if (unitStr.equals("h") || 651 unitStr.equals("hr") || 652 unitStr.equals("hrs") || 653 unitStr.equals("hour") || 654 unitStr.equals("hours")) 655 { 656 integerPortion *= 3600L; 657 unitFromString = TimeUnit.SECONDS; 658 } 659 else if (unitStr.equals("d") || 660 unitStr.equals("day") || 661 unitStr.equals("days")) 662 { 663 integerPortion *= 86400L; 664 unitFromString = TimeUnit.SECONDS; 665 } 666 else 667 { 668 throw new ArgumentException(ERR_DURATION_UNRECOGNIZED_UNIT.get(unitStr)); 669 } 670 671 return timeUnit.convert(integerPortion, unitFromString); 672 } 673 674 675 676 /** 677 * {@inheritDoc} 678 */ 679 @Override() 680 public String getDataTypeName() 681 { 682 return INFO_DURATION_TYPE_NAME.get(); 683 } 684 685 686 687 /** 688 * {@inheritDoc} 689 */ 690 @Override() 691 public String getValueConstraints() 692 { 693 final StringBuilder buffer = new StringBuilder(); 694 buffer.append(INFO_DURATION_CONSTRAINTS_FORMAT.get()); 695 696 if (lowerBoundStr != null) 697 { 698 if (upperBoundStr == null) 699 { 700 buffer.append(" "); 701 buffer.append(INFO_DURATION_CONSTRAINTS_LOWER_BOUND.get(lowerBoundStr)); 702 } 703 else 704 { 705 buffer.append(" "); 706 buffer.append(INFO_DURATION_CONSTRAINTS_LOWER_AND_UPPER_BOUND.get( 707 lowerBoundStr, upperBoundStr)); 708 } 709 } 710 else 711 { 712 if (upperBoundStr != null) 713 { 714 buffer.append(" "); 715 buffer.append(INFO_DURATION_CONSTRAINTS_UPPER_BOUND.get(upperBoundStr)); 716 } 717 } 718 719 return buffer.toString(); 720 } 721 722 723 724 /** 725 * {@inheritDoc} 726 */ 727 @Override() 728 protected void reset() 729 { 730 super.reset(); 731 valueNanos = null; 732 } 733 734 735 736 /** 737 * {@inheritDoc} 738 */ 739 @Override() 740 public DurationArgument getCleanCopy() 741 { 742 return new DurationArgument(this); 743 } 744 745 746 747 /** 748 * Converts the specified number of nanoseconds into a duration string using 749 * the largest possible whole unit (e.g., if the value represents a whole 750 * number of seconds, then the returned string will be expressed in seconds). 751 * 752 * @param nanos The number of nanoseconds to convert to a duration string. 753 * 754 * @return The duration string for the specified number of nanoseconds. 755 */ 756 public static String nanosToDuration(final long nanos) 757 { 758 if (nanos == 86400000000000L) 759 { 760 return "1 day"; 761 } 762 else if ((nanos % 86400000000000L) == 0L) 763 { 764 return (nanos / 86400000000000L) + " days"; 765 } 766 else if (nanos == 3600000000000L) 767 { 768 return "1 hour"; 769 } 770 else if ((nanos % 3600000000000L) == 0L) 771 { 772 return (nanos / 3600000000000L) + " hours"; 773 } 774 else if (nanos == 60000000000L) 775 { 776 return "1 minute"; 777 } 778 else if ((nanos % 60000000000L) == 0L) 779 { 780 return (nanos / 60000000000L) + " minutes"; 781 } 782 else if (nanos == 1000000000L) 783 { 784 return "1 second"; 785 } 786 else if ((nanos % 1000000000L) == 0L) 787 { 788 return (nanos / 1000000000L) + " seconds"; 789 } 790 else if (nanos == 1000000L) 791 { 792 return "1 millisecond"; 793 } 794 else if ((nanos % 1000000L) == 0L) 795 { 796 return (nanos / 1000000L) + " milliseconds"; 797 } 798 else if (nanos == 1000L) 799 { 800 return "1 microsecond"; 801 } 802 else if ((nanos % 1000L) == 0L) 803 { 804 return (nanos / 1000L) + " microseconds"; 805 } 806 else if (nanos == 1L) 807 { 808 return "1 nanosecond"; 809 } 810 else 811 { 812 return nanos + " nanoseconds"; 813 } 814 } 815 816 817 818 /** 819 * {@inheritDoc} 820 */ 821 @Override() 822 protected void addToCommandLine(final List<String> argStrings) 823 { 824 if (valueNanos != null) 825 { 826 argStrings.add(getIdentifierString()); 827 if (isSensitive()) 828 { 829 argStrings.add("***REDACTED***"); 830 } 831 else 832 { 833 argStrings.add(nanosToDuration(valueNanos)); 834 } 835 } 836 } 837 838 839 840 /** 841 * {@inheritDoc} 842 */ 843 @Override() 844 public void toString(final StringBuilder buffer) 845 { 846 buffer.append("DurationArgument("); 847 appendBasicToStringInfo(buffer); 848 849 if (lowerBoundStr != null) 850 { 851 buffer.append(", lowerBound='"); 852 buffer.append(lowerBoundStr); 853 buffer.append('\''); 854 } 855 856 if (upperBoundStr != null) 857 { 858 buffer.append(", upperBound='"); 859 buffer.append(upperBoundStr); 860 buffer.append('\''); 861 } 862 863 if (defaultValueNanos != null) 864 { 865 buffer.append(", defaultValueNanos="); 866 buffer.append(defaultValueNanos); 867 } 868 869 buffer.append(')'); 870 } 871}