001 /*
002 * Copyright 2010-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2010-2016 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 */
021 package com.unboundid.util.args;
022
023
024
025 import java.util.ArrayList;
026 import java.util.Arrays;
027 import java.util.Collections;
028 import java.util.List;
029 import java.util.concurrent.TimeUnit;
030
031 import com.unboundid.util.Debug;
032 import com.unboundid.util.LDAPSDKUsageException;
033 import com.unboundid.util.Mutable;
034 import com.unboundid.util.StaticUtils;
035 import com.unboundid.util.ThreadSafety;
036 import com.unboundid.util.ThreadSafetyLevel;
037
038 import 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)
063 public 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 }