001/* 002 * Copyright 2010-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2010-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.List; 028import java.util.concurrent.TimeUnit; 029 030import com.unboundid.util.Debug; 031import com.unboundid.util.LDAPSDKUsageException; 032import com.unboundid.util.Mutable; 033import com.unboundid.util.StaticUtils; 034import com.unboundid.util.ThreadSafety; 035import com.unboundid.util.ThreadSafetyLevel; 036 037import static com.unboundid.util.args.ArgsMessages.*; 038 039 040 041/** 042 * Creates a new argument that is intended to represent a duration. Duration 043 * values contain an integer portion and a unit portion which represents the 044 * time unit. The unit must be one of the following: 045 * <UL> 046 * <LI>Nanoseconds -- ns, nano, nanos, nanosecond, nanoseconds</LI> 047 * <LI>Microseconds -- us, micro, micros, microsecond, microseconds</LI> 048 * <LI>Milliseconds -- ms, milli, millis, millisecond, milliseconds</LI> 049 * <LI>Seconds -- s, sec, secs, second, seconds</LI> 050 * <LI>Minutes -- m, min, mins, minute, minutes</LI> 051 * <LI>Hours -- h, hr, hrs, hour, hours</LI> 052 * <LI>Days -- d, day, days</LI> 053 * <LI>Weeks -- w, week, weeks</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 switch (lowerBoundUnit) 249 { 250 case NANOSECONDS: 251 lowerBoundStr = minValueNanos + "ns"; 252 break; 253 case MICROSECONDS: 254 lowerBoundStr = lowerBound + "us"; 255 break; 256 case MILLISECONDS: 257 lowerBoundStr = lowerBound + "ms"; 258 break; 259 case SECONDS: 260 lowerBoundStr = lowerBound + "s"; 261 break; 262 case MINUTES: 263 lowerBoundStr = lowerBound + "m"; 264 break; 265 case HOURS: 266 lowerBoundStr = lowerBound + "h"; 267 break; 268 case DAYS: 269 lowerBoundStr = lowerBound + "d"; 270 break; 271 default: 272 throw new LDAPSDKUsageException( 273 ERR_DURATION_UNSUPPORTED_LOWER_BOUND_UNIT.get( 274 lowerBoundUnit.name())); 275 } 276 } 277 278 if (upperBound == null) 279 { 280 maxValueNanos = Long.MAX_VALUE; 281 upperBoundStr = Long.MAX_VALUE + "ns"; 282 } 283 else 284 { 285 if (upperBoundUnit == null) 286 { 287 throw new ArgumentException(ERR_DURATION_UPPER_REQUIRES_UNIT.get( 288 getIdentifierString())); 289 } 290 291 maxValueNanos = upperBoundUnit.toNanos(upperBound); 292 switch (upperBoundUnit) 293 { 294 case NANOSECONDS: 295 upperBoundStr = minValueNanos + "ns"; 296 break; 297 case MICROSECONDS: 298 upperBoundStr = upperBound + "us"; 299 break; 300 case MILLISECONDS: 301 upperBoundStr = upperBound + "ms"; 302 break; 303 case SECONDS: 304 upperBoundStr = upperBound + "s"; 305 break; 306 case MINUTES: 307 upperBoundStr = upperBound + "m"; 308 break; 309 case HOURS: 310 upperBoundStr = upperBound + "h"; 311 break; 312 case DAYS: 313 upperBoundStr = upperBound + "d"; 314 break; 315 default: 316 throw new LDAPSDKUsageException( 317 ERR_DURATION_UNSUPPORTED_UPPER_BOUND_UNIT.get( 318 upperBoundUnit.name())); 319 } 320 } 321 322 if (minValueNanos > maxValueNanos) 323 { 324 throw new ArgumentException(ERR_DURATION_LOWER_GT_UPPER.get( 325 getIdentifierString(), lowerBoundStr, upperBoundStr)); 326 } 327 328 valueNanos = null; 329 validators = new ArrayList<>(5); 330 } 331 332 333 334 /** 335 * Creates a new duration argument that is a "clean" copy of the provided 336 * source argument. 337 * 338 * @param source The source argument to use for this argument. 339 */ 340 private DurationArgument(final DurationArgument source) 341 { 342 super(source); 343 344 defaultValueNanos = source.defaultValueNanos; 345 maxValueNanos = source.maxValueNanos; 346 minValueNanos = source.minValueNanos; 347 lowerBoundStr = source.lowerBoundStr; 348 upperBoundStr = source.upperBoundStr; 349 validators = new ArrayList<>(source.validators); 350 valueNanos = null; 351 } 352 353 354 355 /** 356 * Retrieves the lower bound for this argument using the specified time unit. 357 * 358 * @param unit The time unit in which the lower bound value may be 359 * expressed. 360 * 361 * @return The lower bound for this argument using the specified time unit. 362 */ 363 public long getLowerBound(final TimeUnit unit) 364 { 365 return unit.convert(minValueNanos, TimeUnit.NANOSECONDS); 366 } 367 368 369 370 /** 371 * Retrieves the upper bound for this argument using the specified time unit. 372 * 373 * @param unit The time unit in which the upper bound value may be 374 * expressed. 375 * 376 * @return The upper bound for this argument using the specified time unit. 377 */ 378 public long getUpperBound(final TimeUnit unit) 379 { 380 return unit.convert(maxValueNanos, TimeUnit.NANOSECONDS); 381 } 382 383 384 385 /** 386 * {@inheritDoc} 387 */ 388 @Override() 389 public List<String> getValueStringRepresentations(final boolean useDefault) 390 { 391 final long v; 392 if (valueNanos != null) 393 { 394 v = valueNanos; 395 } 396 else if (useDefault && (defaultValueNanos != null)) 397 { 398 v = defaultValueNanos; 399 } 400 else 401 { 402 return Collections.emptyList(); 403 } 404 405 return Collections.singletonList(nanosToDuration(v)); 406 } 407 408 409 410 /** 411 * {@inheritDoc} 412 */ 413 @Override() 414 protected boolean hasDefaultValue() 415 { 416 return (defaultValueNanos != null); 417 } 418 419 420 421 /** 422 * Retrieves the default value for this argument using the specified time 423 * unit, if defined. 424 * 425 * @param unit The time unit in which the default value should be expressed. 426 * 427 * @return The default value for this argument using the specified time unit, 428 * or {@code null} if none is defined. 429 */ 430 public Long getDefaultValue(final TimeUnit unit) 431 { 432 if (defaultValueNanos == null) 433 { 434 return null; 435 } 436 437 return unit.convert(defaultValueNanos, TimeUnit.NANOSECONDS); 438 } 439 440 441 442 /** 443 * Retrieves the value for this argument using the specified time unit, if one 444 * was provided. 445 * 446 * @param unit The time unit in which to express the value for this 447 * argument. 448 * 449 * @return The value for this argument using the specified time unit. If no 450 * value was provided but a default value was defined, then the 451 * default value will be returned. If no value was provided and no 452 * default value was defined, then {@code null} will be returned. 453 */ 454 public Long getValue(final TimeUnit unit) 455 { 456 if (valueNanos == null) 457 { 458 if (defaultValueNanos == null) 459 { 460 return null; 461 } 462 463 return unit.convert(defaultValueNanos, TimeUnit.NANOSECONDS); 464 } 465 else 466 { 467 return unit.convert(valueNanos, TimeUnit.NANOSECONDS); 468 } 469 } 470 471 472 473 /** 474 * Updates this argument to ensure that the provided validator will be invoked 475 * for any values provided to this argument. This validator will be invoked 476 * after all other validation has been performed for this argument. 477 * 478 * @param validator The argument value validator to be invoked. It must not 479 * be {@code null}. 480 */ 481 public void addValueValidator(final ArgumentValueValidator validator) 482 { 483 validators.add(validator); 484 } 485 486 487 488 /** 489 * {@inheritDoc} 490 */ 491 @Override() 492 protected void addValue(final String valueString) 493 throws ArgumentException 494 { 495 if (valueNanos != null) 496 { 497 throw new ArgumentException( 498 ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(getIdentifierString())); 499 } 500 501 final long proposedValueNanos; 502 try 503 { 504 proposedValueNanos = parseDuration(valueString, TimeUnit.NANOSECONDS); 505 } 506 catch (final ArgumentException ae) 507 { 508 Debug.debugException(ae); 509 throw new ArgumentException( 510 ERR_DURATION_MALFORMED_VALUE.get(valueString, getIdentifierString(), 511 ae.getMessage()), 512 ae); 513 } 514 515 if (proposedValueNanos < minValueNanos) 516 { 517 throw new ArgumentException(ERR_DURATION_BELOW_LOWER_BOUND.get( 518 getIdentifierString(), lowerBoundStr)); 519 } 520 else if (proposedValueNanos > maxValueNanos) 521 { 522 throw new ArgumentException(ERR_DURATION_ABOVE_UPPER_BOUND.get( 523 getIdentifierString(), upperBoundStr)); 524 } 525 else 526 { 527 for (final ArgumentValueValidator v : validators) 528 { 529 v.validateArgumentValue(this, valueString); 530 } 531 532 valueNanos = proposedValueNanos; 533 } 534 } 535 536 537 538 /** 539 * Parses the provided string representation of a duration to a corresponding 540 * numeric representation. 541 * 542 * @param durationString The string representation of the duration to be 543 * parsed. 544 * @param timeUnit The time unit to use for the return value. 545 * 546 * @return The parsed duration as a count in the specified time unit. 547 * 548 * @throws ArgumentException If the provided string cannot be parsed as a 549 * valid duration. 550 */ 551 public static long parseDuration(final String durationString, 552 final TimeUnit timeUnit) 553 throws ArgumentException 554 { 555 // The string must not be empty. 556 final String lowerStr = StaticUtils.toLowerCase(durationString); 557 if (lowerStr.isEmpty()) 558 { 559 throw new ArgumentException(ERR_DURATION_EMPTY_VALUE.get()); 560 } 561 562 // Find the position of the first non-digit character. 563 boolean digitFound = false; 564 boolean nonDigitFound = false; 565 int nonDigitPos = -1; 566 for (int i=0; i < lowerStr.length(); i++) 567 { 568 final char c = lowerStr.charAt(i); 569 if (Character.isDigit(c)) 570 { 571 digitFound = true; 572 } 573 else 574 { 575 nonDigitFound = true; 576 nonDigitPos = i; 577 if (! digitFound) 578 { 579 throw new ArgumentException(ERR_DURATION_NO_DIGIT.get()); 580 } 581 break; 582 } 583 } 584 585 if (! nonDigitFound) 586 { 587 throw new ArgumentException(ERR_DURATION_NO_UNIT.get()); 588 } 589 590 // Separate the integer portion from the unit. 591 long integerPortion = Long.parseLong(lowerStr.substring(0, nonDigitPos)); 592 final String unitStr = lowerStr.substring(nonDigitPos).trim(); 593 594 // Parse the time unit. 595 final TimeUnit unitFromString; 596 if (unitStr.equals("ns") || 597 unitStr.equals("nano") || 598 unitStr.equals("nanos") || 599 unitStr.equals("nanosecond") || 600 unitStr.equals("nanoseconds")) 601 { 602 unitFromString = TimeUnit.NANOSECONDS; 603 } 604 else if (unitStr.equals("us") || 605 unitStr.equals("micro") || 606 unitStr.equals("micros") || 607 unitStr.equals("microsecond") || 608 unitStr.equals("microseconds")) 609 { 610 unitFromString = TimeUnit.MICROSECONDS; 611 } 612 else if (unitStr.equals("ms") || 613 unitStr.equals("milli") || 614 unitStr.equals("millis") || 615 unitStr.equals("millisecond") || 616 unitStr.equals("milliseconds")) 617 { 618 unitFromString = TimeUnit.MILLISECONDS; 619 } 620 else if (unitStr.equals("s") || 621 unitStr.equals("sec") || 622 unitStr.equals("secs") || 623 unitStr.equals("second") || 624 unitStr.equals("seconds")) 625 { 626 unitFromString = TimeUnit.SECONDS; 627 } 628 else if (unitStr.equals("m") || 629 unitStr.equals("min") || 630 unitStr.equals("mins") || 631 unitStr.equals("minute") || 632 unitStr.equals("minutes")) 633 { 634 integerPortion *= 60L; 635 unitFromString = TimeUnit.SECONDS; 636 } 637 else if (unitStr.equals("h") || 638 unitStr.equals("hr") || 639 unitStr.equals("hrs") || 640 unitStr.equals("hour") || 641 unitStr.equals("hours")) 642 { 643 integerPortion *= 3600L; 644 unitFromString = TimeUnit.SECONDS; 645 } 646 else if (unitStr.equals("d") || 647 unitStr.equals("day") || 648 unitStr.equals("days")) 649 { 650 integerPortion *= 86_400L; 651 unitFromString = TimeUnit.SECONDS; 652 } 653 else if (unitStr.equals("w") || 654 unitStr.equals("week") || 655 unitStr.equals("weeks")) 656 { 657 integerPortion *= 604_800; 658 unitFromString = TimeUnit.SECONDS; 659 } 660 else 661 { 662 throw new ArgumentException(ERR_DURATION_UNRECOGNIZED_UNIT.get(unitStr)); 663 } 664 665 return timeUnit.convert(integerPortion, unitFromString); 666 } 667 668 669 670 /** 671 * {@inheritDoc} 672 */ 673 @Override() 674 public String getDataTypeName() 675 { 676 return INFO_DURATION_TYPE_NAME.get(); 677 } 678 679 680 681 /** 682 * {@inheritDoc} 683 */ 684 @Override() 685 public String getValueConstraints() 686 { 687 final StringBuilder buffer = new StringBuilder(); 688 buffer.append(INFO_DURATION_CONSTRAINTS_FORMAT.get()); 689 690 if (lowerBoundStr != null) 691 { 692 if (upperBoundStr == null) 693 { 694 buffer.append(" "); 695 buffer.append(INFO_DURATION_CONSTRAINTS_LOWER_BOUND.get(lowerBoundStr)); 696 } 697 else 698 { 699 buffer.append(" "); 700 buffer.append(INFO_DURATION_CONSTRAINTS_LOWER_AND_UPPER_BOUND.get( 701 lowerBoundStr, upperBoundStr)); 702 } 703 } 704 else 705 { 706 if (upperBoundStr != null) 707 { 708 buffer.append(" "); 709 buffer.append(INFO_DURATION_CONSTRAINTS_UPPER_BOUND.get(upperBoundStr)); 710 } 711 } 712 713 return buffer.toString(); 714 } 715 716 717 718 /** 719 * {@inheritDoc} 720 */ 721 @Override() 722 protected void reset() 723 { 724 super.reset(); 725 valueNanos = null; 726 } 727 728 729 730 /** 731 * {@inheritDoc} 732 */ 733 @Override() 734 public DurationArgument getCleanCopy() 735 { 736 return new DurationArgument(this); 737 } 738 739 740 741 /** 742 * Converts the specified number of nanoseconds into a duration string using 743 * the largest possible whole unit (e.g., if the value represents a whole 744 * number of seconds, then the returned string will be expressed in seconds). 745 * 746 * @param nanos The number of nanoseconds to convert to a duration string. 747 * 748 * @return The duration string for the specified number of nanoseconds. 749 */ 750 public static String nanosToDuration(final long nanos) 751 { 752 if (nanos == 0) 753 { 754 return "0 nanoseconds"; 755 } 756 757 if (nanos == 604_800_000_000_000L) 758 { 759 return "1 week"; 760 } 761 else if ((nanos % 604_800_000_000_000L) == 0L) 762 { 763 return (nanos / 604_800_000_000_000L) + " weeks"; 764 } 765 else if (nanos == 86_400_000_000_000L) 766 { 767 return "1 day"; 768 } 769 else if ((nanos % 86_400_000_000_000L) == 0L) 770 { 771 return (nanos / 86_400_000_000_000L) + " days"; 772 } 773 else if (nanos == 3_600_000_000_000L) 774 { 775 return "1 hour"; 776 } 777 else if ((nanos % 3_600_000_000_000L) == 0L) 778 { 779 return (nanos / 3_600_000_000_000L) + " hours"; 780 } 781 else if (nanos == 60_000_000_000L) 782 { 783 return "1 minute"; 784 } 785 else if ((nanos % 60_000_000_000L) == 0L) 786 { 787 return (nanos / 60_000_000_000L) + " minutes"; 788 } 789 else if (nanos == 1_000_000_000L) 790 { 791 return "1 second"; 792 } 793 else if ((nanos % 1_000_000_000L) == 0L) 794 { 795 return (nanos / 1_000_000_000L) + " seconds"; 796 } 797 else if (nanos == 1_000_000L) 798 { 799 return "1 millisecond"; 800 } 801 else if ((nanos % 1_000_000L) == 0L) 802 { 803 return (nanos / 1_000_000L) + " milliseconds"; 804 } 805 else if (nanos == 1000L) 806 { 807 return "1 microsecond"; 808 } 809 else if ((nanos % 1000L) == 0L) 810 { 811 return (nanos / 1000L) + " microseconds"; 812 } 813 else if (nanos == 1L) 814 { 815 return "1 nanosecond"; 816 } 817 else 818 { 819 return nanos + " nanoseconds"; 820 } 821 } 822 823 824 825 /** 826 * {@inheritDoc} 827 */ 828 @Override() 829 protected void addToCommandLine(final List<String> argStrings) 830 { 831 if (valueNanos != null) 832 { 833 argStrings.add(getIdentifierString()); 834 if (isSensitive()) 835 { 836 argStrings.add("***REDACTED***"); 837 } 838 else 839 { 840 argStrings.add(nanosToDuration(valueNanos)); 841 } 842 } 843 } 844 845 846 847 /** 848 * {@inheritDoc} 849 */ 850 @Override() 851 public void toString(final StringBuilder buffer) 852 { 853 buffer.append("DurationArgument("); 854 appendBasicToStringInfo(buffer); 855 856 if (lowerBoundStr != null) 857 { 858 buffer.append(", lowerBound='"); 859 buffer.append(lowerBoundStr); 860 buffer.append('\''); 861 } 862 863 if (upperBoundStr != null) 864 { 865 buffer.append(", upperBound='"); 866 buffer.append(upperBoundStr); 867 buffer.append('\''); 868 } 869 870 if (defaultValueNanos != null) 871 { 872 buffer.append(", defaultValueNanos="); 873 buffer.append(defaultValueNanos); 874 } 875 876 buffer.append(')'); 877 } 878}