1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package de.fu_berlin.ties.util;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.InvocationTargetException;
28 import java.text.NumberFormat;
29 import java.util.Arrays;
30 import java.util.Locale;
31 import java.util.Random;
32
33 import org.apache.commons.lang.BooleanUtils;
34 import org.apache.commons.lang.StringUtils;
35 import org.apache.commons.lang.builder.StandardToStringStyle;
36 import org.apache.commons.lang.builder.ToStringBuilder;
37 import org.apache.log.Hierarchy;
38 import org.apache.log.LogTarget;
39 import org.apache.log.Logger;
40 import org.apache.log.Priority;
41 import org.apache.log.filter.PriorityFilter;
42 import org.apache.log.format.ExtendedPatternFormatter;
43 import org.apache.log.output.io.FileTarget;
44 import org.apache.log.output.io.StreamTarget;
45
46 import de.fu_berlin.ties.TiesConfiguration;
47
48 /***
49 * A static class that provides general utility methods.
50 * No instances of this class can be created, only the static members
51 * should be used.
52 *
53 * @author Christian Siefkes
54 * @version $Revision: 1.27 $, $Date: 2004/12/06 17:59:41 $, $Author: siefkes $
55 */
56 public final class Util {
57
58 /***
59 * Filter used by the logger to write messages to standard output.
60 */
61 private static final PriorityFilter STD_FILTERED;
62
63 /***
64 * The logger used in the TIE system. The
65 * <a href="http://avalon.apache.org/logkit/">Avalon LogKit</a> (bundled
66 * with Velocity) is used for logging. All log messages are written to a log
67 * file; message with priority <code>INFO</code> or higher are also written
68 * to standard out.
69 */
70 public static final Logger LOG =
71 Hierarchy.getDefaultHierarchy().getLoggerFor("ties");
72
73 /***
74 * Configuration key: Only messages with this priority or higher are logged.
75 */
76 public static final String CONFIG_LOGGER_LOG = "log.log";
77
78 /***
79 * Configuration key: Only messages with this priority or higher are
80 * written to standard output (but only if covered by
81 * {@link #CONFIG_LOGGER_LOG}).
82 */
83 public static final String CONFIG_LOGGER_SHOW = "log.show";
84
85 /***
86 * String recognized as Not-a-Number when parsing floating-point numbers:
87 * {@value} (ignoring case).
88 */
89 public static final String NAN = "NaN";
90
91 /***
92 * Number format used by {@link #showDuration(long)} to format seconds.
93 * This is meant for log methods so we always use the English locale.
94 * Should be synchronized on itself.
95 */
96 private static final NumberFormat SEC_FORMAT =
97 NumberFormat.getNumberInstance(Locale.ENGLISH);
98
99 /***
100 * Number format used by the {@link #format(double) format} methods.
101 * Uses the English locale. Should be synchronized on itself.
102 */
103 private static final NumberFormat NUM_FORMAT =
104 NumberFormat.getNumberInstance(Locale.ENGLISH);
105
106 /***
107 * Static initializer: modify style used by {@link ToStringBuilder} and
108 * configure number format + logger.
109 */
110 static {
111
112 final StandardToStringStyle tiesStyle = new StandardToStringStyle();
113 tiesStyle.setUseShortClassName(true);
114 tiesStyle.setUseIdentityHashCode(false);
115 tiesStyle.setFieldSeparator(";");
116 ToStringBuilder.setDefaultStyle(tiesStyle);
117
118
119 SEC_FORMAT.setMaximumFractionDigits(3);
120
121
122
123 final String basePattern =
124 "%{priority}: %{message}//n%{throwable}";
125
126 final String fullPattern =
127 "%{time:yyyy-MM-dd HH:mm:ss.SSS} [%{thread}] " + basePattern;
128
129
130 final String logFileName = "ties.log";
131 LogTarget fileLog;
132 IOException caughtException = null;
133 Util.LOG.setPriority(Priority.DEBUG);
134
135
136 final LogTarget stdRaw = new StreamTarget(System.out,
137 new ExtendedPatternFormatter(basePattern));
138 STD_FILTERED = new PriorityFilter(Priority.INFO);
139 STD_FILTERED.addTarget(stdRaw);
140
141 try {
142 fileLog = new FileTarget(new File(logFileName), false,
143 new ExtendedPatternFormatter(fullPattern));
144 } catch (IOException ioe) {
145
146 caughtException = ioe;
147 fileLog = null;
148 }
149
150 final LogTarget[] targets;
151 if (fileLog != null) {
152 targets = new LogTarget[] {fileLog, STD_FILTERED};
153 } else {
154
155 targets = new LogTarget[] {stdRaw};
156 }
157
158 Util.LOG.setLogTargets(targets);
159
160 if (caughtException != null) {
161 Util.LOG.error("Log initialization: could not open " + logFileName
162 + " -- will log to standard out only: " + caughtException);
163 }
164 }
165
166 /***
167 * Appends a non-negative number to a string buffer.
168 *
169 * @param appender the string buffer to append to
170 * @param number the number to append; must not be negative
171 * @param minDigits the minimum number of digits to print
172 * @param prefix a prefix to print in front of the number, if not
173 * <code>null</code>
174 * @param appendAlways if <code>true</code>, the number is always printed;
175 * otherwise only positive numbers are printed -- in this case the appender
176 * is not modified if the number is 0
177 * @throws IllegalArgumentException if <code>number < 0</code>
178 */
179 public static void appendPositiveNumber(final StringBuffer appender,
180 final long number, final int minDigits, final String prefix,
181 final boolean appendAlways)
182 throws IllegalArgumentException {
183
184 ensureNonNegative(number, "Number");
185
186 if (appendAlways || (number != 0)) {
187
188 if (StringUtils.isNotEmpty(prefix)) {
189 appender.append(prefix);
190 }
191
192
193 final String numberString = Long.toString(number);
194 final int lengthDiff = minDigits - numberString.length();
195
196
197 for (int i = 0; i < lengthDiff; i++) {
198 appender.append('0');
199 }
200
201
202 appender.append(numberString);
203 }
204 }
205
206 /***
207 * Converts an object into a boolean primitive. For a {@link Boolean}
208 * object, the contained boolean primitive is returned.
209 *
210 * <p>Otherwise the object's <code>toString()</code> output is parsed
211 * calling {@link BooleanUtils#toBooleanObject(java.lang.String)}, i.e.
212 * The strings "true", "on", and "yes" are accepted for a <code>true</code>
213 * value; "false", "off", and "no" are accepted for <code>false</code>;
214 * case and surrounding whitespace are ignored. An exception is thrown for
215 * all other strings.
216 *
217 * <p>If the specified object is <code>null</code>, <code>false</code>
218 * is returned.
219 *
220 * @param obj the object to check
221 * @return the converted boolean
222 * @throws IllegalArgumentException if the object's <code>toString()</code>
223 * output cannot be parsed as a boolean
224 */
225 public static boolean asBoolean(final Object obj)
226 throws IllegalArgumentException {
227 final boolean result;
228 if (obj instanceof Boolean) {
229
230 result = ((Boolean) obj).booleanValue();
231 } else if (obj != null) {
232
233 final String raw = obj.toString().trim();
234 final Boolean parsed = BooleanUtils.toBooleanObject(raw);
235
236 if (parsed == null) {
237
238 throw new IllegalArgumentException("String '" + raw
239 + "' does not specify a boolean value");
240 } else {
241 result = parsed.booleanValue();
242 }
243 } else {
244
245 result = false;
246 }
247 return result;
248 }
249
250 /***
251 * Converts an object into a byte primitive. For a {@link Number}
252 * object, the contained number is converted to a byte -- this may involve
253 * rounding or truncation.
254 * <p>Otherwise the object's <code>toString()</code> output is parsed
255 * using the {@link Byte#decode(java.lang.String)} method.
256 * <p>If the specified object is <code>null</code>, -1 is returned.
257 *
258 * @param obj the object to check
259 * @return the converted byte
260 * @throws NumberFormatException if the object's <code>toString()</code>
261 * output does not contain a parsable byte
262 */
263 public static byte asByte(final Object obj) throws NumberFormatException {
264 final byte result;
265 if (obj instanceof Number) {
266
267 result = ((Number) obj).byteValue();
268 } else if (obj != null) {
269
270 final String raw = obj.toString().trim();
271 result = Byte.decode(raw).byteValue();
272 } else {
273
274 result = -1;
275 }
276 return result;
277 }
278
279 /***
280 * Converts an object into a char primitive. For a {@link Character}
281 * object, the contained char primitive is returned.
282 * <p>Otherwise the first non-whitespace character of the
283 * object's <code>toString()</code> output is returned. An exception is
284 * thrown if the string is empty or contains only whitespace.
285 * <p>If the specified object is <code>null</code>,
286 * {@link Character#MIN_VALUE} (the smallest character value) is returned.
287 *
288 * @param obj the object to check
289 * @return the converted char
290 * @throws IndexOutOfBoundsException if the object's <code>toString()</code>
291 * output is the empty string (after trimming outer whitespace)
292 */
293 public static char asChar(final Object obj)
294 throws IndexOutOfBoundsException {
295 final char result;
296 if (obj instanceof Character) {
297
298 result = ((Character) obj).charValue();
299 } else if (obj != null) {
300
301 final String raw = obj.toString().trim();
302 result = raw.charAt(0);
303 } else {
304
305 result = Character.MIN_VALUE;
306 }
307 return result;
308 }
309
310 /***
311 * Converts an object into a double primitive. For a {@link Number}
312 * object, the contained number is converted to a double -- this may involve
313 * rounding.
314 *
315 * <p>Otherwise the object's <code>toString()</code> output is parsed
316 * using the {@link Double#parseDouble(java.lang.String)} method.
317 * If the string value to equal to {@link #NAN} (ignoring case),
318 * {@link Double#NaN} is returned.
319 *
320 * <p>If the specified object is <code>null</code>, -1.0 is returned.
321 *
322 * @param obj the object to check
323 * @return the converted double
324 * @throws NumberFormatException if the object's <code>toString()</code>
325 * output does not contain a parsable double
326 */
327 public static double asDouble(final Object obj)
328 throws NumberFormatException {
329 final double result;
330 if (obj instanceof Number) {
331
332 result = ((Number) obj).doubleValue();
333 } else if (obj != null) {
334 final String raw = obj.toString().trim();
335
336 if (NAN.equalsIgnoreCase(raw)) {
337
338 result = Double.NaN;
339 } else {
340
341 result = Double.parseDouble(raw);
342 }
343 } else {
344
345 result = -1.0;
346 }
347 return result;
348 }
349
350 /***
351 * Converts an object into a float primitive. For a {@link Number}
352 * object, the contained number is converted to a float -- this may involve
353 * rounding.
354 *
355 * <p>Otherwise the object's <code>toString()</code> output is parsed
356 * using the {@link Float#parseFloat(java.lang.String)} method.
357 * If the string value to equal to {@link #NAN} (ignoring case),
358 * {@link Float#NaN} is returned.
359 *
360 * <p>If the specified object is <code>null</code>, -1 is returned.
361 *
362 * @param obj the object to check
363 * @return the converted float
364 * @throws NumberFormatException if the object's <code>toString()</code>
365 * output does not contain a parsable float
366 */
367 public static float asFloat(final Object obj)
368 throws NumberFormatException {
369 final float result;
370 if (obj instanceof Number) {
371
372 result = ((Number) obj).floatValue();
373 } else if (obj != null) {
374 final String raw = obj.toString().trim();
375
376 if (NAN.equalsIgnoreCase(raw)) {
377
378 result = Float.NaN;
379 } else {
380
381 result = Float.parseFloat(raw);
382 }
383 } else {
384
385 result = -1.0f;
386 }
387 return result;
388 }
389
390 /***
391 * Converts an object into an integer primitive. For a {@link Number}
392 * object, the contained number is converted to a int -- this may involve
393 * rounding or truncation.
394 * <p>Otherwise the object's <code>toString()</code> output is parsed
395 * using the {@link Integer#decode(java.lang.String)} method.
396 * <p>If the specified object is <code>null</code>, -1 is returned.
397 *
398 * @param obj the object to check
399 * @return the converted int
400 * @throws NumberFormatException if the object's <code>toString()</code>
401 * output does not contain a parsable int
402 */
403 public static int asInt(final Object obj) throws NumberFormatException {
404 final int result;
405 if (obj instanceof Number) {
406
407 result = ((Number) obj).intValue();
408 } else if (obj != null) {
409
410 final String raw = obj.toString().trim();
411 result = Integer.decode(raw).intValue();
412 } else {
413
414 result = -1;
415 }
416 return result;
417 }
418
419 /***
420 * Converts an object into a long primitive. For a {@link Number}
421 * object, the contained number is converted to a long -- this may involve
422 * rounding or truncation.
423 * <p>Otherwise the object's <code>toString()</code> output is parsed
424 * using the {@link Long#decode(java.lang.String)} method.
425 * <p>If the specified object is <code>null</code>, -1 is returned.
426 *
427 * @param obj the object to check
428 * @return the converted long
429 * @throws NumberFormatException if the object's <code>toString()</code>
430 * output does not contain a parsable long
431 */
432 public static long asLong(final Object obj) throws NumberFormatException {
433 final long result;
434 if (obj instanceof Number) {
435
436 result = ((Number) obj).longValue();
437 } else if (obj != null) {
438
439 final String raw = obj.toString().trim();
440 result = Long.decode(raw).longValue();
441 } else {
442
443 result = -1;
444 }
445 return result;
446 }
447
448 /***
449 * Converts an object into a short primitive. For a {@link Number}
450 * object, the contained number is converted to a short -- this may involve
451 * rounding or truncation.
452 * <p>Otherwise the object's <code>toString()</code> output is parsed
453 * using the {@link Short#decode(java.lang.String)} method.
454 * <p>If the specified object is <code>null</code>, -1 is returned.
455 *
456 * @param obj the object to check
457 * @return the converted short
458 * @throws NumberFormatException if the object's <code>toString()</code>
459 * output does not contain a parsable short
460 */
461 public static short asShort(final Object obj) throws NumberFormatException {
462 final short result;
463 if (obj instanceof Number) {
464
465 result = ((Number) obj).shortValue();
466 } else if (obj != null) {
467
468 final String raw = obj.toString().trim();
469 result = Short.decode(raw).shortValue();
470 } else {
471
472 result = -1;
473 }
474 return result;
475 }
476
477 /***
478 * Converts an object into a String. For non-null objects, the output
479 * of the {@link Object#toString()} is returned; otherwise,
480 * <code>null</code> is returned.
481 *
482 * @param obj the object to check
483 * @return the output of the object's {@link Object#toString()}; or
484 * <code>null</code> if <code>obj</code> is <code>null</code>
485 */
486 public static String asString(final Object obj) {
487 if (obj != null) {
488 return obj.toString();
489 } else {
490 return null;
491 }
492 }
493
494 /***
495 * Reconfigures the {@link #LOG logger} from the {@link #CONFIG_LOGGER_LOG}
496 * and {@link #CONFIG_LOGGER_SHOW} values in the provided configuration.
497 * This method should be used with care since it affects the global logging.
498 * It is called by {@link de.fu_berlin.ties.Main#main(String[])} after
499 * configurations and command-line propertis have been read.
500 *
501 * @param config the configuration to use
502 */
503 public static void configureLog(final TiesConfiguration config) {
504
505 LOG.setPriority(Priority.getPriorityForName(config.getString(
506 CONFIG_LOGGER_LOG).toUpperCase()));
507 STD_FILTERED.setPriority(Priority.getPriorityForName(config.getString(
508 CONFIG_LOGGER_SHOW).toUpperCase()));
509 }
510
511 /***
512 * Delegates to {@link Util#createObject(Class, String[])}, reading the
513 * class name from the first element in the array.
514 *
515 * @param classDef defines the class to create: the first element must be
516 * a string giving the fully qualified class name; any further elements
517 * are passed as constructor parameters
518 * @return the created object; the returned object will be an instance of
519 * the specified type
520 * @throws IllegalArgumentException if the class name is missing (array is
521 * empty)
522 * @throws ClassNotFoundException if the class cannot be located
523 * @throws InstantiationException if instantiation failed
524 * @throws SecurityException if access to the required reflection
525 * information is denied
526 */
527 public static Object createObject(final String[] classDef)
528 throws IllegalArgumentException, ClassNotFoundException,
529 InstantiationException, SecurityException {
530 final int length = classDef.length;
531
532
533 if (length == 0) {
534 throw new IllegalArgumentException(
535 "Class name missing (empty class definition)");
536 }
537
538
539 final String type = classDef[0];
540 final String[] params = new String[length - 1];
541
542 for (int i = 0; i < params.length; i++) {
543 params[i] = classDef[i + 1];
544 }
545
546 return createObject(Class.forName(type), params);
547 }
548
549 /***
550 * Delegates to {@link Util#createObject(Class, Object[], Class)}, setting
551 * the <code>paramType</code> to the {@link String} class.
552 *
553 * @param type the class of the object to create
554 * @param params the objects in this array are passed to the constructor;
555 * if <code>null</code> or empty, the default constructor is used
556 * @return the created object; the returned object will be an instance of
557 * <code>type</code>
558 * @throws InstantiationException if instantiation failed
559 * @throws SecurityException if access to the required reflection
560 * information is denied
561 */
562 public static Object createObject(final Class type, final String[] params)
563 throws InstantiationException, SecurityException {
564 return createObject(type, params, String.class);
565 }
566
567 /***
568 * Creates an object of a specified type. The object must have a public
569 * constructor that accepts <code>params.length</code> arguments of the
570 * specified <code>paramType</code>.
571 *
572 * @param type the class of the object to create
573 * @param params the objects in this array are passed to the constructor;
574 * if <code>null</code> or empty, the default constructor is used
575 * @param paramType the type of the elements stored in the
576 * <code>params</code> array
577 * @return the created object; the returned object will be an instance of
578 * <code>type</code>
579 * @throws InstantiationException if instantiation failed
580 * @throws SecurityException if access to the required reflection
581 * information is denied
582 */
583 public static Object createObject(final Class type, final Object[] params,
584 final Class paramType)
585 throws InstantiationException, SecurityException {
586 final Class[] paramClasses = new Class[params.length];
587 Arrays.fill(paramClasses, paramType);
588 return createObject(type, params, paramClasses);
589 }
590
591 /***
592 * Creates an object of a specified type. The object must have a public
593 * constructor that accepts <code>params.length</code> arguments of the
594 * specified types.
595 *
596 * @param type the class of the object to create
597 * @param params the objects in this array are passed to the constructor;
598 * if <code>null</code> or empty, the default constructor is used
599 * @param paramTypes the types of the elements stored in the
600 * <code>params</code> list
601 * @return the created object; the returned object will be an instance of
602 * <code>type</code>
603 * @throws IllegalArgumentException if <code>params</code> and
604 * <code>paramTypes</code> are of different lengths
605 * @throws InstantiationException if instantiation failed
606 * @throws SecurityException if access to the required reflection
607 * information is denied
608 */
609 public static Object createObject(final Class type, final Object[] params,
610 final Class[] paramTypes) throws IllegalArgumentException,
611 InstantiationException, SecurityException {
612
613 if ((params != null) && (params.length != paramTypes.length)) {
614 throw new IllegalArgumentException("Lengths of given arrays of "
615 + " parameters and parameter types differ: " + params.length
616 + ", " + paramTypes.length);
617 }
618
619 Object result;
620 try {
621 if ((params == null) || (params.length == 0)) {
622
623 result = type.newInstance();
624 } else {
625 final Constructor cons = type.getConstructor(paramTypes);
626
627
628 result = cons.newInstance(params);
629 }
630 } catch (IllegalAccessException iae) {
631 final InstantiationException ie =
632 new InstantiationException(iae.toString());
633 ie.initCause(iae);
634 throw ie;
635 } catch (InvocationTargetException ite) {
636 final InstantiationException ie =
637 new InstantiationException(ite.toString());
638 ie.initCause(ite);
639 throw ie;
640 } catch (ExceptionInInitializerError eiie) {
641 final InstantiationException ie =
642 new InstantiationException(eiie.toString());
643 ie.initCause(eiie);
644 throw ie;
645 } catch (NoSuchMethodException nsme) {
646 final InstantiationException ie =
647 new InstantiationException(nsme.toString());
648 ie.initCause(nsme);
649 throw ie;
650 }
651 return result;
652 }
653
654 /***
655 * Formats a duration. The generated string has the form
656 * "<i>days</i>D+<i>hours</i>:<i>minutes</i>:<i>secs</i>.<i>millisecs</i>".
657 * Days and milliseconds are omitted if 0; the time fields are omitted if
658 * all of them are 0, but only if days are present (to avoid returning an
659 * empty string). Samples: 2D+03:02:43.666, 01:00:00, 1D, 00:05:00.123,
660 * 1D+04:19:03, 00:00:00.
661 *
662 * @param msecs the number of milliseconds
663 * @return a formatted representation as specified above
664 */
665 public static String formatDurationInMillisecs(final long msecs) {
666
667 if (msecs < 0.0) {
668 throw new IllegalArgumentException(
669 "Negative durations are not supported: " + msecs);
670 }
671
672 final int milliRange = 1000;
673 final int secMinRange = 60;
674 final int hourRange = 24;
675
676 final int millis = (int) (msecs % milliRange);
677 long rest = msecs / milliRange;
678 final int seconds = (int) (rest % secMinRange);
679 rest = rest / secMinRange;
680 final int minutes = (int) (rest % secMinRange);
681 rest /= secMinRange;
682 final int hours = (int) (rest % hourRange);
683 rest /= hourRange;
684
685 final StringBuffer result = new StringBuffer();
686
687
688 if (rest > 0) {
689 result.append(rest).append("D");
690 }
691
692
693
694 if ((rest == 0) || (hours != 0) || (minutes != 0) || (seconds != 0)
695 || (millis != 0)) {
696
697
698 if (rest > 0) {
699 result.append("+");
700 }
701
702
703 appendPositiveNumber(result, hours, 2, null, true);
704 appendPositiveNumber(result, minutes, 2, ":", true);
705 appendPositiveNumber(result, seconds, 2, ":", true);
706
707
708 appendPositiveNumber(result, millis, 3, ".", false);
709 }
710
711 return result.toString();
712 }
713
714 /***
715 * Formats a duration. The generated string has the form
716 * "<i>days</i>D+<i>hours</i>:<i>minutes</i>:<i>secs</i>.<i>millisecs</i>".
717 * Days and milliseconds are omitted if 0; the time fields are omitted if
718 * all of them are 0, but only if days are present (to avoid returning an
719 * empty string). Samples: 2D+03:02:43.666, 01:00:00, 1D, 00:05:00.123,
720 * 1D+04:19:03, 00:00:00.
721 *
722 * @param secs the number of seconds; must be nonnegative
723 * @return a formatted representation as specified above
724 * @throws IllegalArgumentException if <code>secs < 0</code>
725 */
726 public static String formatDurationInSeconds(final double secs)
727 throws IllegalArgumentException {
728 return formatDurationInMillisecs(Math.round(secs * 1000));
729 }
730
731 /***
732 * Ensures that a number is positive or 0, throwing an exception if this
733 * condition is violated.
734 *
735 * @param number the number to check
736 * @param identifier string used to describe the number in the thrown
737 * exception
738 * @throws IllegalArgumentException if <code>number < 0.0</code>
739 */
740 public static void ensureNonNegative(final double number,
741 final String identifier) throws IllegalArgumentException {
742 if (number < 0.0) {
743 throw new IllegalArgumentException(identifier
744 + " must not be negative: " + number);
745 }
746 }
747
748 /***
749 * Ensures that a number is positive or 0, throwing an exception if this
750 * condition is violated.
751 *
752 * @param number the number to check
753 * @param identifier string used to describe the number in the thrown
754 * exception
755 * @throws IllegalArgumentException if <code>number < 0</code>
756 */
757 public static void ensureNonNegative(final long number,
758 final String identifier) throws IllegalArgumentException {
759 if (number < 0) {
760 throw new IllegalArgumentException(identifier
761 + " must not be negative: " + number);
762 }
763 }
764
765 /***
766 * Formats the given number, using at most 4 fraction digits.
767 *
768 * @param number the number for format
769 * @return the formatted number
770 */
771 public static String format(final double number) {
772 return format(number, 4);
773 }
774
775 /***
776 * Formats the given number, using at most the specified number of
777 * fraction digits.
778 *
779 * @param number the number for format
780 * @param maxFractDigits maximum number of digits allowed in the fraction
781 * portion of the number
782 * @return the formatted number
783 */
784 public static String format(final double number, final int maxFractDigits) {
785 synchronized (NUM_FORMAT) {
786 NUM_FORMAT.setMaximumFractionDigits(maxFractDigits);
787 return NUM_FORMAT.format(number);
788 }
789 }
790
791 /***
792 * Returns an instance of a pseudo-random number generator that uses a
793 * fixed seed, so the same sequence of calls to each object created
794 * by this method will yield the same sequence of pseudo-random numbers.
795 *
796 * @return a new source of pseudo-randomness that will return
797 * reproducable pseudo-random numbers
798 */
799 public static Random reproducibleRandom() {
800
801 return new Random(4127208836813110743L);
802 }
803
804 /***
805 * Calculated and prints the time passed since a given start time
806 * (in English).
807 *
808 * @param startTime the start time in milliseconds
809 * @return a formatted string containing the time difference between the
810 * start time and the {@linkplain System#currentTimeMillis() current time}
811 */
812 public static String showDuration(final long startTime) {
813 final long timeInMS = System.currentTimeMillis() - startTime;
814 final double timeInSecs = ((double) timeInMS) / 1000;
815 String result;
816
817 synchronized (SEC_FORMAT) {
818 result = "elapsed time: " + SEC_FORMAT.format(timeInSecs) + "s";
819 }
820
821
822 if (timeInSecs >= 300) {
823 result += " = " + formatDurationInSeconds(timeInSecs);
824 }
825
826 return result;
827 }
828
829 /***
830 * <code>Util</code> instances should NOT be constructed in standard
831 * programming. Instead, the method of this class should be called in a
832 * static way. This constructor is public to permit tools that require a
833 * JavaBean instance to operate.
834 */
835 public Util() {
836 super();
837 }
838
839 }