001: /*
002: * ArchiveUtils
003: *
004: * $Header: /cvsroot/archive-crawler/ArchiveOpenCrawler/src/java/org/archive/util/ArchiveUtils.java,v 1.38 2007/01/23 00:29:48 gojomo Exp $
005: *
006: * Created on Jul 7, 2003
007: *
008: * Copyright (C) 2003 Internet Archive.
009: *
010: * This file is part of the Heritrix web crawler (crawler.archive.org).
011: *
012: * Heritrix is free software; you can redistribute it and/or modify
013: * it under the terms of the GNU Lesser Public License as published by
014: * the Free Software Foundation; either version 2.1 of the License, or
015: * any later version.
016: *
017: * Heritrix is distributed in the hope that it will be useful,
018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
020: * GNU Lesser Public License for more details.
021: *
022: * You should have received a copy of the GNU Lesser Public License
023: * along with Heritrix; if not, write to the Free Software
024: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
025: *
026: */
027: package org.archive.util;
028:
029: import java.io.IOException;
030: import java.io.PrintWriter;
031: import java.io.StringWriter;
032: import java.text.NumberFormat;
033: import java.text.ParseException;
034: import java.text.SimpleDateFormat;
035: import java.util.Calendar;
036: import java.util.Date;
037: import java.util.GregorianCalendar;
038: import java.util.Locale;
039: import java.util.TimeZone;
040:
041: /**
042: * Miscellaneous useful methods.
043: *
044: * @author gojomo & others
045: */
046: public class ArchiveUtils {
047:
048: /**
049: * Arc-style date stamp in the format yyyyMMddHHmm and UTC time zone.
050: */
051: private static final ThreadLocal<SimpleDateFormat> TIMESTAMP12 = threadLocalDateFormat("yyyyMMddHHmm");;
052:
053: /**
054: * Arc-style date stamp in the format yyyyMMddHHmmss and UTC time zone.
055: */
056: private static final ThreadLocal<SimpleDateFormat> TIMESTAMP14 = threadLocalDateFormat("yyyyMMddHHmmss");
057: /**
058: * Arc-style date stamp in the format yyyyMMddHHmmssSSS and UTC time zone.
059: */
060: private static final ThreadLocal<SimpleDateFormat> TIMESTAMP17 = threadLocalDateFormat("yyyyMMddHHmmssSSS");
061:
062: /**
063: * Log-style date stamp in the format yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
064: * UTC time zone is assumed.
065: */
066: private static final ThreadLocal<SimpleDateFormat> TIMESTAMP17ISO8601Z = threadLocalDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
067:
068: /**
069: * Log-style date stamp in the format yyyy-MM-dd'T'HH:mm:ss'Z'
070: * UTC time zone is assumed.
071: */
072: private static final ThreadLocal<SimpleDateFormat> TIMESTAMP14ISO8601Z = threadLocalDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
073:
074: /**
075: * Default character to use padding strings.
076: */
077: private static final char DEFAULT_PAD_CHAR = ' ';
078:
079: /** milliseconds in an hour */
080: private static final int HOUR_IN_MS = 60 * 60 * 1000;
081: /** milliseconds in a day */
082: private static final int DAY_IN_MS = 24 * HOUR_IN_MS;
083:
084: private static ThreadLocal<SimpleDateFormat> threadLocalDateFormat(
085: final String pattern) {
086: ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>() {
087: protected SimpleDateFormat initialValue() {
088: SimpleDateFormat df = new SimpleDateFormat(pattern);
089: df.setTimeZone(TimeZone.getTimeZone("GMT"));
090: return df;
091: }
092: };
093: return tl;
094: }
095:
096: public static int MAX_INT_CHAR_WIDTH = Integer.toString(
097: Integer.MAX_VALUE).length();
098:
099: /**
100: * Utility function for creating arc-style date stamps
101: * in the format yyyMMddHHmmssSSS.
102: * Date stamps are in the UTC time zone
103: * @return the date stamp
104: */
105: public static String get17DigitDate() {
106: return TIMESTAMP17.get().format(new Date());
107: }
108:
109: /**
110: * Utility function for creating arc-style date stamps
111: * in the format yyyMMddHHmmss.
112: * Date stamps are in the UTC time zone
113: * @return the date stamp
114: */
115: public static String get14DigitDate() {
116: return TIMESTAMP14.get().format(new Date());
117: }
118:
119: /**
120: * Utility function for creating arc-style date stamps
121: * in the format yyyMMddHHmm.
122: * Date stamps are in the UTC time zone
123: * @return the date stamp
124: */
125: public static String get12DigitDate() {
126: return TIMESTAMP12.get().format(new Date());
127: }
128:
129: /**
130: * Utility function for creating log timestamps, in
131: * W3C/ISO8601 format, assuming UTC. Use current time.
132: *
133: * Format is yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
134: *
135: * @return the date stamp
136: */
137: public static String getLog17Date() {
138: return TIMESTAMP17ISO8601Z.get().format(new Date());
139: }
140:
141: /**
142: * Utility function for creating log timestamps, in
143: * W3C/ISO8601 format, assuming UTC.
144: *
145: * Format is yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
146: * @param date Date to format.
147: *
148: * @return the date stamp
149: */
150: public static String getLog17Date(long date) {
151: return TIMESTAMP17ISO8601Z.get().format(new Date(date));
152: }
153:
154: /**
155: * Utility function for creating log timestamps, in
156: * W3C/ISO8601 format, assuming UTC. Use current time.
157: *
158: * Format is yyyy-MM-dd'T'HH:mm:ss'Z'
159: *
160: * @return the date stamp
161: */
162: public static String getLog14Date() {
163: return TIMESTAMP14ISO8601Z.get().format(new Date());
164: }
165:
166: /**
167: * Utility function for creating log timestamps, in
168: * W3C/ISO8601 format, assuming UTC.
169: *
170: * Format is yyyy-MM-dd'T'HH:mm:ss'Z'
171: * @param date long timestamp to format.
172: *
173: * @return the date stamp
174: */
175: public static String getLog14Date(long date) {
176: return TIMESTAMP14ISO8601Z.get().format(new Date(date));
177: }
178:
179: /**
180: * Utility function for creating log timestamps, in
181: * W3C/ISO8601 format, assuming UTC.
182: *
183: * Format is yyyy-MM-dd'T'HH:mm:ss'Z'
184: * @param date Date to format.
185: *
186: * @return the date stamp
187: */
188: public static String getLog14Date(Date date) {
189: return TIMESTAMP14ISO8601Z.get().format(date);
190: }
191:
192: /**
193: * Utility function for creating arc-style date stamps
194: * in the format yyyyMMddHHmmssSSS.
195: * Date stamps are in the UTC time zone
196: *
197: * @param date milliseconds since epoc
198: * @return the date stamp
199: */
200: public static String get17DigitDate(long date) {
201: return TIMESTAMP17.get().format(new Date(date));
202: }
203:
204: public static String get17DigitDate(Date date) {
205: return TIMESTAMP17.get().format(date);
206: }
207:
208: /**
209: * Utility function for creating arc-style date stamps
210: * in the format yyyyMMddHHmmss.
211: * Date stamps are in the UTC time zone
212: *
213: * @param date milliseconds since epoc
214: * @return the date stamp
215: */
216: public static String get14DigitDate(long date) {
217: return TIMESTAMP14.get().format(new Date(date));
218: }
219:
220: public static String get14DigitDate(Date d) {
221: return TIMESTAMP14.get().format(d);
222: }
223:
224: /**
225: * Utility function for creating arc-style date stamps
226: * in the format yyyyMMddHHmm.
227: * Date stamps are in the UTC time zone
228: *
229: * @param date milliseconds since epoc
230: * @return the date stamp
231: */
232: public static String get12DigitDate(long date) {
233: return TIMESTAMP12.get().format(new Date(date));
234: }
235:
236: public static String get12DigitDate(Date d) {
237: return TIMESTAMP12.get().format(d);
238: }
239:
240: /**
241: * Parses an ARC-style date. If passed String is < 12 characters in length,
242: * we pad. At a minimum, String should contain a year (>=4 characters).
243: * Parse will also fail if day or month are incompletely specified. Depends
244: * on the above getXXDigitDate methods.
245: * @param A 4-17 digit date in ARC style (<code>yyyy</code> to
246: * <code>yyyyMMddHHmmssSSS</code>) formatting.
247: * @return A Date object representing the passed String.
248: * @throws ParseException
249: */
250: public static Date getDate(String d) throws ParseException {
251: Date date = null;
252: if (d == null) {
253: throw new IllegalArgumentException("Passed date is null");
254: }
255: switch (d.length()) {
256: case 14:
257: date = ArchiveUtils.parse14DigitDate(d);
258: break;
259:
260: case 17:
261: date = ArchiveUtils.parse17DigitDate(d);
262: break;
263:
264: case 12:
265: date = ArchiveUtils.parse12DigitDate(d);
266: break;
267:
268: case 0:
269: case 1:
270: case 2:
271: case 3:
272: throw new ParseException(
273: "Date string must at least contain a" + "year: "
274: + d, d.length());
275:
276: default:
277: if (!(d.startsWith("19") || d.startsWith("20"))) {
278: throw new ParseException("Unrecognized century: " + d,
279: 0);
280: }
281: if (d.length() < 8 && (d.length() % 2) != 0) {
282: throw new ParseException("Incomplete month/date: " + d,
283: d.length());
284: }
285: StringBuilder sb = new StringBuilder(d);
286: if (sb.length() < 8) {
287: for (int i = sb.length(); sb.length() < 8; i += 2) {
288: sb.append("01");
289: }
290: }
291: if (sb.length() < 12) {
292: for (int i = sb.length(); sb.length() < 12; i++) {
293: sb.append("0");
294: }
295: }
296: date = ArchiveUtils.parse12DigitDate(sb.toString());
297: }
298:
299: return date;
300: }
301:
302: /**
303: * Utility function for parsing arc-style date stamps
304: * in the format yyyMMddHHmmssSSS.
305: * Date stamps are in the UTC time zone. The whole string will not be
306: * parsed, only the first 17 digits.
307: *
308: * @param date an arc-style formatted date stamp
309: * @return the Date corresponding to the date stamp string
310: * @throws ParseException if the inputstring was malformed
311: */
312: public static Date parse17DigitDate(String date)
313: throws ParseException {
314: return TIMESTAMP17.get().parse(date);
315: }
316:
317: /**
318: * Utility function for parsing arc-style date stamps
319: * in the format yyyMMddHHmmss.
320: * Date stamps are in the UTC time zone. The whole string will not be
321: * parsed, only the first 14 digits.
322: *
323: * @param date an arc-style formatted date stamp
324: * @return the Date corresponding to the date stamp string
325: * @throws ParseException if the inputstring was malformed
326: */
327: public static Date parse14DigitDate(String date)
328: throws ParseException {
329: return TIMESTAMP14.get().parse(date);
330: }
331:
332: /**
333: * Utility function for parsing arc-style date stamps
334: * in the format yyyMMddHHmm.
335: * Date stamps are in the UTC time zone. The whole string will not be
336: * parsed, only the first 12 digits.
337: *
338: * @param date an arc-style formatted date stamp
339: * @return the Date corresponding to the date stamp string
340: * @throws ParseException if the inputstring was malformed
341: */
342: public static Date parse12DigitDate(String date)
343: throws ParseException {
344: return TIMESTAMP12.get().parse(date);
345: }
346:
347: /**
348: * Convert 17-digit date format timestamps (as found in crawl.log, for
349: * example) into a GregorianCalendar object. + * Useful so you can convert
350: * into milliseconds-since-epoch. Note: it is possible to compute
351: * milliseconds-since-epoch + * using {@link #parse17DigitDate}.UTC(), but
352: * that method is deprecated in favor of using Calendar.getTimeInMillis(). + *
353: * <p/>I probably should have dug into all the utility methods in
354: * DateFormat.java to parse the timestamp, but this was + * easier. If
355: * someone wants to fix this to use those methods, please have at it! <p/>
356: * Mike Schwartz, schwartz at CodeOnTheRoad dot com.
357: *
358: * @param timestamp17String
359: * @return Calendar set to <code>timestamp17String</code>.
360: */
361: public static Calendar timestamp17ToCalendar(
362: String timestamp17String) {
363: GregorianCalendar calendar = new GregorianCalendar();
364: int year = Integer.parseInt(timestamp17String.substring(0, 4));
365: int dayOfMonth = Integer.parseInt(timestamp17String.substring(
366: 6, 8));
367: // Month is 0-based
368: int month = Integer.parseInt(timestamp17String.substring(4, 6)) - 1;
369: int hourOfDay = Integer.parseInt(timestamp17String.substring(8,
370: 10));
371: int minute = Integer.parseInt(timestamp17String.substring(10,
372: 12));
373: int second = Integer.parseInt(timestamp17String.substring(12,
374: 14));
375: int milliseconds = Integer.parseInt(timestamp17String
376: .substring(14, 17));
377: calendar.set(Calendar.YEAR, year);
378: calendar.set(Calendar.MONTH, month);
379: calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
380: calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
381: calendar.set(Calendar.MINUTE, minute);
382: calendar.set(Calendar.SECOND, second);
383: calendar.set(Calendar.MILLISECOND, milliseconds);
384: return calendar;
385: }
386:
387: /**
388: * @param timestamp A 14-digit timestamp or the suffix for a 14-digit
389: * timestamp: E.g. '20010909014640' or '20010101' or '1970'.
390: * @return Seconds since the epoch as a string zero-pre-padded so always
391: * Integer.MAX_VALUE wide (Makes it so sorting of resultant string works
392: * properly).
393: * @throws ParseException
394: */
395: public static String secondsSinceEpoch(String timestamp)
396: throws ParseException {
397: return zeroPadInteger((int) (getSecondsSinceEpoch(timestamp)
398: .getTime() / 1000));
399: }
400:
401: /**
402: * @param timestamp A 14-digit timestamp or the suffix for a 14-digit
403: * timestamp: E.g. '20010909014640' or '20010101' or '1970'.
404: * @return A date.
405: * @see #secondsSinceEpoch(String)
406: * @throws ParseException
407: */
408: public static Date getSecondsSinceEpoch(String timestamp)
409: throws ParseException {
410: if (timestamp.length() < 14) {
411: if (timestamp.length() < 10
412: && (timestamp.length() % 2) == 1) {
413: throw new IllegalArgumentException("Must have year, "
414: + "month, date, hour or second granularity: "
415: + timestamp);
416: }
417: if (timestamp.length() == 4) {
418: // Add first month and first date.
419: timestamp = timestamp + "01010000";
420: }
421: if (timestamp.length() == 6) {
422: // Add a date of the first.
423: timestamp = timestamp + "010000";
424: }
425: if (timestamp.length() < 14) {
426: timestamp = timestamp
427: + ArchiveUtils.padTo("", 14 - timestamp
428: .length(), '0');
429: }
430: }
431: return ArchiveUtils.parse14DigitDate(timestamp);
432: }
433:
434: /**
435: * @param i Integer to add prefix of zeros too. If passed
436: * 2005, will return the String <code>0000002005</code>. String
437: * width is the width of Integer.MAX_VALUE as a string (10
438: * digits).
439: * @return Padded String version of <code>i</code>.
440: */
441: public static String zeroPadInteger(int i) {
442: return ArchiveUtils.padTo(Integer.toString(i),
443: MAX_INT_CHAR_WIDTH, '0');
444: }
445:
446: /**
447: * Convert an <code>int</code> to a <code>String</code>, and pad it to
448: * <code>pad</code> spaces.
449: * @param i the int
450: * @param pad the width to pad to.
451: * @return String w/ padding.
452: */
453: public static String padTo(final int i, final int pad) {
454: String n = Integer.toString(i);
455: return padTo(n, pad);
456: }
457:
458: /**
459: * Pad the given <code>String</code> to <code>pad</code> characters wide
460: * by pre-pending spaces. <code>s</code> should not be <code>null</code>.
461: * If <code>s</code> is already wider than <code>pad</code> no change is
462: * done.
463: *
464: * @param s the String to pad
465: * @param pad the width to pad to.
466: * @return String w/ padding.
467: */
468: public static String padTo(final String s, final int pad) {
469: return padTo(s, pad, DEFAULT_PAD_CHAR);
470: }
471:
472: /**
473: * Pad the given <code>String</code> to <code>pad</code> characters wide
474: * by pre-pending <code>padChar</code>.
475: *
476: * <code>s</code> should not be <code>null</code>. If <code>s</code> is
477: * already wider than <code>pad</code> no change is done.
478: *
479: * @param s the String to pad
480: * @param pad the width to pad to.
481: * @param padChar The pad character to use.
482: * @return String w/ padding.
483: */
484: public static String padTo(final String s, final int pad,
485: final char padChar) {
486: String result = s;
487: int l = s.length();
488: if (l < pad) {
489: StringBuffer sb = new StringBuffer(pad);
490: while (l < pad) {
491: sb.append(padChar);
492: l++;
493: }
494: sb.append(s);
495: result = sb.toString();
496: }
497: return result;
498: }
499:
500: /** check that two byte arrays are equal. They may be <code>null</code>.
501: *
502: * @param lhs a byte array
503: * @param rhs another byte array.
504: * @return <code>true</code> if they are both equal (or both
505: * <code>null</code>)
506: */
507: public static boolean byteArrayEquals(final byte[] lhs,
508: final byte[] rhs) {
509: if (lhs == null && rhs != null || lhs != null && rhs == null) {
510: return false;
511: }
512: if (lhs == rhs) {
513: return true;
514: }
515: if (lhs.length != rhs.length) {
516: return false;
517: }
518: for (int i = 0; i < lhs.length; i++) {
519: if (lhs[i] != rhs[i]) {
520: return false;
521: }
522: }
523: return true;
524: }
525:
526: /**
527: * Converts a double to a string.
528: * @param val The double to convert
529: * @param precision How many characters to include after '.'
530: * @return the double as a string.
531: */
532: public static String doubleToString(double val,
533: int maxFractionDigits) {
534: return doubleToString(val, maxFractionDigits, 0);
535: }
536:
537: private static String doubleToString(double val,
538: int maxFractionDigits, int minFractionDigits) {
539: NumberFormat f = NumberFormat.getNumberInstance(Locale.US);
540: f.setMaximumFractionDigits(maxFractionDigits);
541: f.setMinimumFractionDigits(minFractionDigits);
542: return f.format(val);
543: }
544:
545: /**
546: * Takes a byte size and formats it for display with 'friendly' units.
547: * <p>
548: * This involves converting it to the largest unit
549: * (of B, KB, MB, GB, TB) for which the amount will be > 1.
550: * <p>
551: * Additionally, at least 2 significant digits are always displayed.
552: * <p>
553: * Displays as bytes (B): 0-1023
554: * Displays as kilobytes (KB): 1024 - 2097151 (~2Mb)
555: * Displays as megabytes (MB): 2097152 - 4294967295 (~4Gb)
556: * Displays as gigabytes (GB): 4294967296 - infinity
557: * <p>
558: * Negative numbers will be returned as '0 B'.
559: *
560: * @param amount the amount of bytes
561: * @return A string containing the amount, properly formated.
562: */
563: public static String formatBytesForDisplay(long amount) {
564: double displayAmount = (double) amount;
565: int unitPowerOf1024 = 0;
566:
567: if (amount <= 0) {
568: return "0 B";
569: }
570:
571: while (displayAmount >= 1024 && unitPowerOf1024 < 4) {
572: displayAmount = displayAmount / 1024;
573: unitPowerOf1024++;
574: }
575:
576: // TODO: get didactic, make these KiB, MiB, GiB, TiB
577: final String[] units = { " B", " KB", " MB", " GB", " TB" };
578:
579: // ensure at least 2 significant digits (#.#) for small displayValues
580: int fractionDigits = (displayAmount < 10) ? 1 : 0;
581: return doubleToString(displayAmount, fractionDigits,
582: fractionDigits)
583: + units[unitPowerOf1024];
584: }
585:
586: /**
587: * Convert milliseconds value to a human-readable duration
588: * @param time
589: * @return Human readable string version of passed <code>time</code>
590: */
591: public static String formatMillisecondsToConventional(long time) {
592: return formatMillisecondsToConventional(time, true);
593: }
594:
595: /**
596: * Convert milliseconds value to a human-readable duration
597: * @param time
598: * @param toMs whether to print to the ms
599: * @return Human readable string version of passed <code>time</code>
600: */
601: public static String formatMillisecondsToConventional(long time,
602: boolean toMs) {
603: StringBuffer sb = new StringBuffer();
604: if (time < 0) {
605: sb.append("-");
606: }
607: long absTime = Math.abs(time);
608: if (!toMs && absTime < 1000) {
609: return "0s";
610: }
611: if (absTime > DAY_IN_MS) {
612: // days
613: sb.append(absTime / DAY_IN_MS + "d");
614: absTime = absTime % DAY_IN_MS;
615: }
616: if (absTime > HOUR_IN_MS) {
617: //got hours.
618: sb.append(absTime / HOUR_IN_MS + "h");
619: absTime = absTime % HOUR_IN_MS;
620: }
621: if (absTime > 60000) {
622: sb.append(absTime / 60000 + "m");
623: absTime = absTime % 60000;
624: }
625: if (absTime > 1000) {
626: sb.append(absTime / 1000 + "s");
627: absTime = absTime % 1000;
628: }
629: if (toMs) {
630: sb.append(absTime + "ms");
631: }
632: return sb.toString();
633: }
634:
635: /**
636: * Generate a long UID based on the given class and version number.
637: * Using this instead of the default will assume serialization
638: * compatibility across class changes unless version number is
639: * intentionally bumped.
640: *
641: * @param class1
642: * @param version
643: * @return UID based off class and version number.
644: */
645: public static long classnameBasedUID(Class class1, int version) {
646: String callingClassname = class1.getName();
647: return (long) callingClassname.hashCode() << 32 + version;
648: }
649:
650: /**
651: * Copy the raw bytes of a long into a byte array, starting at
652: * the specified offset.
653: *
654: * @param l
655: * @param array
656: * @param offset
657: */
658: public static void longIntoByteArray(long l, byte[] array,
659: int offset) {
660: int i, shift;
661:
662: for (i = 0, shift = 56; i < 8; i++, shift -= 8)
663: array[offset + i] = (byte) (0xFF & (l >> shift));
664: }
665:
666: public static long byteArrayIntoLong(byte[] bytearray) {
667: return byteArrayIntoLong(bytearray, 0);
668: }
669:
670: /**
671: * Byte array into long.
672: * @param bytearray Array to convert to a long.
673: * @param offset Offset into array at which we start decoding the long.
674: * @return Long made of the bytes of <code>array</code> beginning at
675: * offset <code>offset</code>.
676: * @see #longIntoByteArray(long, byte[], int)
677: */
678: public static long byteArrayIntoLong(byte[] bytearray, int offset) {
679: long result = 0;
680: for (int i = offset; i < 8 /*Bytes in long*/; i++) {
681: result = (result << 8 /*Bits in byte*/)
682: | (0xff & (byte) (bytearray[i] & 0xff));
683: }
684: return result;
685: }
686:
687: /**
688: * Given a string that may be a plain host or host/path (without
689: * URI scheme), add an implied http:// if necessary.
690: *
691: * @param u string to evaluate
692: * @return string with http:// added if no scheme already present
693: */
694: public static String addImpliedHttpIfNecessary(String u) {
695: if (u.indexOf(':') == -1 || u.indexOf('.') < u.indexOf(':')) {
696: // No scheme present; prepend "http://"
697: u = "http://" + u;
698: }
699: return u;
700: }
701:
702: /**
703: * Verify that the array begins with the prefix.
704: *
705: * @param array
706: * @param prefix
707: * @return true if array is identical to prefix for the first prefix.length
708: * positions
709: */
710: public static boolean startsWith(byte[] array, byte[] prefix) {
711: if (prefix.length > array.length) {
712: return false;
713: }
714: for (int i = 0; i < prefix.length; i++) {
715: if (array[i] != prefix[i]) {
716: return false;
717: }
718: }
719: return true;
720: }
721:
722: /**
723: * Utility method to get a String singleLineReport from Reporter
724: * @param rep Reporter to get singleLineReport from
725: * @return String of report
726: */
727: public static String singleLineReport(Reporter rep) {
728: StringWriter sw = new StringWriter();
729: PrintWriter pw = new PrintWriter(sw);
730: try {
731: rep.singleLineReportTo(pw);
732: } catch (IOException e) {
733: // not really possible
734: e.printStackTrace();
735: }
736: pw.flush();
737: return sw.toString();
738: }
739:
740: /**
741: * Compose the requested report into a String. DANGEROUS IF REPORT
742: * CAN BE LARGE.
743: *
744: * @param rep Reported
745: * @param name String name of report to compose
746: * @return String of report
747: */
748: public static String writeReportToString(Reporter rep, String name) {
749: StringWriter sw = new StringWriter();
750: PrintWriter pw = new PrintWriter(sw);
751: rep.reportTo(name, pw);
752: pw.flush();
753: return sw.toString();
754: }
755: }
|