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