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