001 package org.rakeshv.utils;
002
003 import java.io.BufferedReader;
004 import java.io.BufferedWriter;
005 import java.io.FileInputStream;
006 import java.io.FileOutputStream;
007 import java.io.FileReader;
008 import java.io.FileWriter;
009 import java.io.FileNotFoundException;
010 import java.io.InputStream;
011 import java.io.InputStreamReader;
012 import java.io.OutputStreamWriter;
013 import java.io.IOException;
014 import java.io.PrintWriter;
015 import java.io.UnsupportedEncodingException;
016 import java.net.Authenticator;
017 import java.net.HttpURLConnection;
018 import java.net.MalformedURLException;
019 import java.net.PasswordAuthentication;
020 import java.net.URL;
021 import java.util.Enumeration;
022 import java.util.Properties;
023 import java.util.zip.GZIPInputStream;
024
025 /**
026 * Provides utility methods to return <code>String</code> objects
027 * from commonly used resources.
028 *
029 * <p>Copyright 2004-2006 Rakesh Vidyadharan</p>
030 * @author Rakesh Vidyadharan 2004 August 27
031 * @version $Id: StringUtilities.java,v 1.15 2006/03/14 22:55:36 rakesh Exp $
032 */
033 public final class StringUtilities extends Object
034 {
035 /**
036 * The <code>end of line</code> characters to use in the returned
037 * String objects.
038 */
039 public static final String endOfLine =
040 System.getProperty( "line.separator" );
041
042 /**
043 * Default constructor. Cannot be instantiated.
044 */
045 private StringUtilities()
046 {
047 super();
048 }
049
050 /**
051 * Return the response body from the specified <code>HTTP URL</code>
052 * as a <code>String</code> using the encoding specified by the
053 * <code>web server</code>.
054 *
055 * <p>The following code snippet shows how to use this method:</p>
056 * <pre>
057 * import org.rakeshv.utils.StringUtilities;
058 *
059 * try
060 * {
061 * String result = StringUtilities.fromUrl( "http://www.w3.org/" );
062 * }
063 * catch ( Throwable t )
064 * {
065 * // Exception handling
066 * }
067 * </pre>
068 *
069 * @see #readResponseFromUrl( HttpURLConnection )
070 * @param url The <code>URL</code> from which the contents are to be
071 * retrieved.
072 * @throws MalformedURLException If the specified <code>url</code>
073 * is improperly formatted.
074 * @throws IOException If errors are encountered while reading the
075 * data. An IOException is thrown with the error response from the
076 * server if a HTTP error is encountered.
077 */
078 public static final String fromUrl( String url )
079 throws MalformedURLException, IOException
080 {
081 String result = null;
082 HttpURLConnection httpConnection = null;
083
084 try
085 {
086 httpConnection =
087 (HttpURLConnection) ( new URL( url ) ).openConnection();
088 httpConnection.setRequestProperty( "Accept-Encoding", "gzip" );
089 result = readResponseFromUrl( httpConnection );
090 }
091 finally
092 {
093 httpConnection.disconnect();
094 }
095
096 return result;
097 }
098
099 /**
100 * Return the response body from the specified <code>HTTP URL</code>
101 * as a <code>String</code> using the encoding specified by the
102 * <code>web server</code>. The response is obtained by
103 * <code>POST</code>'ing the data specified in the
104 * <code>Properties</code> object.
105 *
106 * <p>The following code snippet shows how to use this method:</p>
107 * <pre>
108 * import java.util.Properties;
109 * import org.rakeshv.utils.StringUtilities;
110 *
111 * Properties properties = new Properties();
112 * properties.setProperty( "page", "2" );
113 * properties.setProperty( "user", "scott" );
114 * properties.setProperty( "password", "tiger" );
115 * try
116 * {
117 * String result = StringUtilities.fromUrl(
118 * "http://www.w3.org/", properties );
119 * }
120 * catch ( Throwable t )
121 * {
122 * // Exception handling
123 * }
124 * </pre>
125 *
126 * @see #readResponseFromUrl( HttpURLConnection )
127 * @param url The <code>URL</code> from which the contents are to be
128 * retrieved.
129 * @param properties A <code>Properties</code> object with the
130 * name-value pairs that are to be submitted to the server. A
131 * Properties object is used to ensure that the keys and values
132 * are <code>String</code> objects.
133 * @throws MalformedURLException If the specified <code>url</code>
134 * is improperly formatted.
135 * @throws IOException If errors are encountered while reading the
136 * data. An IOException is thrown with the error response from the
137 * server if a HTTP error is encountered.
138 */
139 public static final String fromUrl( String url,
140 Properties properties ) throws MalformedURLException, IOException
141 {
142 String result = null;
143 HttpURLConnection httpConnection = null;
144 StringBuilder body = new StringBuilder();
145
146 if ( properties != null )
147 {
148 for ( Enumeration enumeration = properties.propertyNames();
149 enumeration.hasMoreElements(); )
150 {
151 String name = (String) enumeration.nextElement();
152 body.append( name );
153 body.append( "=" );
154 body.append( properties.getProperty( name ) );
155 body.append( "&" );
156 }
157 }
158
159 try
160 {
161 httpConnection =
162 (HttpURLConnection) ( new URL( url ) ).openConnection();
163 httpConnection.setRequestMethod( "POST" );
164 httpConnection.setAllowUserInteraction( false );
165 httpConnection.setDoOutput( true );
166 httpConnection.setRequestProperty( "Accept-Encoding", "gzip" );
167 httpConnection.setRequestProperty(
168 "Content-type", "application/x-www-form-urlencoded" );
169 httpConnection.setRequestProperty( "Content-length",
170 String.valueOf( body.length() - 1 ) );
171
172 if ( body.length() > 0 )
173 {
174 PrintWriter writer =
175 new PrintWriter( httpConnection.getOutputStream() );
176 writer.print( body.substring( 0, body.length() - 1 ) );
177 writer.flush();
178 writer.close();
179 }
180
181 result = readResponseFromUrl( httpConnection );
182 }
183 finally
184 {
185 httpConnection.disconnect();
186 }
187
188 return result;
189 }
190
191 /**
192 * Return the response body from the specified <code>HTTP URL</code>
193 * as a <code>String</code> using the encoding specified by the
194 * <code>web server</code>. Uses the specified credentials to
195 * authenticate the request with the server.
196 *
197 * <p>The following code snippet shows how to use this method:</p>
198 * <pre>
199 * import org.rakeshv.utils.StringUtilities;
200 *
201 * try
202 * {
203 * String url = "http://www.w3.org/";
204 * String userName = "scott";
205 * String password = "tiger";
206 * String result = StringUtilities.fromUrl( url, userName, password );
207 * }
208 * catch ( Throwable t )
209 * {
210 * // Exception handling
211 * }
212 * </pre>
213 *
214 * @see #setAuthenticator( String, String )
215 * @see #fromUrl( String )
216 * @param url The <code>URL</code> from which the contents are to be
217 * retrieved.
218 * @param userName The user name to use for authentication with the
219 * server.
220 * @param password The password to use for authentication with the
221 * server.
222 * @throws MalformedURLException If the specified <code>url</code>
223 * is improperly formatted.
224 * @throws IOException If errors are encountered while reading the
225 * data. An IOException is thrown with the error response from the
226 * server if a HTTP error is encountered.
227 */
228 public static final String fromUrl( String url, String userName,
229 String password ) throws MalformedURLException, IOException
230 {
231 setAuthenticator( userName, password );
232 return fromUrl( url );
233 }
234
235 /**
236 * Return the response body from the specified <code>HTTP URL</code>
237 * as a <code>String</code> using the encoding specified by the
238 * <code>web server</code>. The response is obtained by
239 * <code>POST</code>'ing the data specified in the
240 * <code>Properties</code> object. The <code>userName</code> and
241 * <code>password</code> values specified are used to authenticate
242 * the request with the server.
243 *
244 * <p>The following code snippet shows how to use this method:</p>
245 * <pre>
246 * import java.util.Properties;
247 * import org.rakeshv.utils.StringUtilities;
248 *
249 * String url = "http://www.w3.org/";
250 * String user = "scott";
251 * String password = "tiger";
252 * Properties properties = new Properties();
253 * properties.setProperty( "page", "2" );
254 * properties.setProperty( "content", "xml" );
255 * properties.setProperty( "details", "heavy" );
256 * try
257 * {
258 * String result = StringUtilities.fromUrl(
259 * url, properties, user, password );
260 * }
261 * catch ( Throwable t )
262 * {
263 * // Exception handling
264 * }
265 * </pre>
266 *
267 * @see #setAuthenticator( String, String )
268 * @see #fromUrl( String, Properties )
269 * @param url The <code>URL</code> from which the contents are to be
270 * retrieved.
271 * @param properties A <code>Properties</code> object with the
272 * name-value pairs that are to be submitted to the server. A
273 * Properties object is used to ensure that the keys and values
274 * are <code>String</code> objects.
275 * @param userName The user name to use for authentication with the
276 * server.
277 * @param password The password to use for authentication with the
278 * server.
279 * @throws MalformedURLException If the specified <code>url</code>
280 * is improperly formatted.
281 * @throws IOException If errors are encountered while reading the
282 * data. An IOException is thrown with the error response from the
283 * server if a HTTP error is encountered.
284 */
285 public static final String fromUrl( String url,
286 Properties properties, String userName, String password )
287 throws MalformedURLException, IOException
288 {
289 setAuthenticator( userName, password );
290 return fromUrl( url, properties );
291 }
292
293 /**
294 * Parse the <code>charset</code> from the <code>content-type</code>
295 * HTTP header if it was specified.
296 *
297 * @param type The <code>content-type</code> header value.
298 * @return String - The encoding value if specified. If it was not
299 * specified, this method returns <code>null</code>.
300 */
301 public static final String parseEncoding( String type )
302 {
303 if ( type == null ) return type;
304
305 StringBuilder builder = new StringBuilder();
306 int length = type.length();
307 int index = type.indexOf( "charset" );
308 if ( index != -1 )
309 {
310 index += 8; // Advance to after charset=
311 char currentChar = type.charAt( index );
312
313 // Skip leading quotes if any
314 if ( currentChar == '"' || currentChar == '\'' )
315 {
316 ++index;
317 }
318
319 currentChar = type.charAt( index );
320 while ( currentChar != '\r' &&
321 currentChar != '\n' &&
322 currentChar != ' ' &&
323 currentChar != ';' &&
324 currentChar != '\'' &&
325 currentChar != '"' &&
326 ++index < length )
327 {
328 builder.append( currentChar );
329 currentChar = type.charAt( index );
330 }
331
332 // Add last character if there were no quotes or such like
333 if ( index == length )
334 {
335 builder.append( currentChar );
336 }
337 }
338
339 String result = null;
340 if ( builder.length() > 0 )
341 {
342 result = builder.toString().toUpperCase();
343 }
344
345 return result;
346 }
347
348 /**
349 * Return the contents of the specified <code>file</code> as a
350 * <code>String</code> using the system default encoding.
351 *
352 * <p>The following code snippet shows how to use this method:</p>
353 * <pre>
354 * import org.rakeshv.utils.StringUtilities;
355 *
356 * try
357 * {
358 * String result = StringUtilities.fromFile( "/tmp/test.java" );
359 * }
360 * catch ( Throwable t )
361 * {
362 * // Exception handling
363 * }
364 * </pre>
365 *
366 * @param file The <code>file</code> from which the contents are to
367 * be read.
368 * @throws FileNotFoundException If the specified <code>file</code>
369 * could not be found.
370 * @throws IOException If errors are encountered while reading the
371 * data.
372 */
373 public static final String fromFile( String file )
374 throws FileNotFoundException, IOException
375 {
376 BufferedReader reader = new BufferedReader(
377 new FileReader( file ) );
378 String result = readData( reader );
379 reader.close();
380
381 return result;
382 }
383
384 /**
385 * Return the contents of the specified <code>file</code> as a
386 * <code>String</code> using the character encoding specified.
387 *
388 * <p>The following code snippet shows how to use this method:</p>
389 * <pre>
390 * import org.rakeshv.utils.StringUtilities;
391 *
392 * try
393 * {
394 * String result = StringUtilities.fromFile( "/tmp/test.java", "UTF-8" );
395 * }
396 * catch ( Throwable t )
397 * {
398 * // Exception handling
399 * }
400 * </pre>
401 *
402 * @param file The <code>file</code> from which the contents are to
403 * be read.
404 * @param encoding The <code>character encoding</code> scheme to use
405 * to read the file contents.
406 * @throws FileNotFoundException If the specified <code>file</code>
407 * could not be found.
408 * @throws UnsupportedEncodingException If the specified character
409 * encoding is invalid.
410 * @throws IOException If errors are encountered while reading the
411 * data.
412 */
413 public static final String fromFile( String file, String encoding )
414 throws FileNotFoundException, IOException
415 {
416 BufferedReader reader = new BufferedReader(
417 new InputStreamReader(
418 new FileInputStream( file ), encoding ) );
419 String result = readData( reader );
420 reader.close();
421
422 return result;
423 }
424
425 /**
426 * Write the contents of the specified <code>String</code> to the
427 * <code>file</code> specified using the system default encoding.
428 *
429 * <p>The following code snippet shows how to use this method:</p>
430 * <pre>
431 * import org.rakeshv.utils.StringUtilities;
432 *
433 * try
434 * {
435 * String content = "blah .... blah";
436 * StringUtilities.toFile( content, "/tmp/test.txt" );
437 * }
438 * catch ( Throwable t )
439 * {
440 * // Exception handling
441 * }
442 * </pre>
443 *
444 * @since version 1.16
445 * @param content The content that is to be written to file.
446 * @param file The <code>fully qualified file name</code> to which
447 * the contents are to be written.
448 * @throws IOException If errors are encountered while writing the
449 * data.
450 */
451 public static final void toFile( String content, String file )
452 throws IOException
453 {
454 BufferedWriter writer = new BufferedWriter(
455 new FileWriter( file ) );
456 writer.write( content, 0, content.length() );
457 if ( ! content.endsWith( "\n" ) )
458 {
459 writer.newLine();
460 }
461 writer.close();
462 }
463
464 /**
465 * Write the contents of the specified <code>String</code> to the
466 * <code>file</code> specified using the character encoding specified.
467 *
468 * <p>The following code snippet shows how to use this method:</p>
469 * <pre>
470 * import org.rakeshv.utils.StringUtilities;
471 *
472 * try
473 * {
474 * String content = "blah ... blah";
475 * StringUtilities.toFile( content, "/tmp/test.txt", "UTF-8" );
476 * }
477 * catch ( Throwable t )
478 * {
479 * // Exception handling
480 * }
481 * </pre>
482 *
483 * @since version 1.16
484 * @param content The content that is to be written to file.
485 * @param file The <code>file</code> from which the contents are to
486 * be read.
487 * @param encoding The <code>character encoding</code> scheme to use
488 * to read the file contents.
489 * @throws UnsupportedEncodingException If the specified character
490 * encoding is invalid.
491 * @throws IOException If errors are encountered while reading the
492 * data.
493 */
494 public static final void toFile( String content, String file,
495 String encoding )
496 throws UnsupportedEncodingException, IOException
497 {
498 BufferedWriter writer = new BufferedWriter(
499 new OutputStreamWriter(
500 new FileOutputStream( file ), encoding ) );
501 writer.write( content, 0, content.length() );
502 if ( ! content.endsWith( "\n" ) )
503 {
504 writer.newLine();
505 }
506 writer.close();
507 }
508
509 /**
510 * Returns the <code>StackTrace</code> for the specified instance
511 * of <code>Throwable</code>.
512 *
513 * @param throwable The instance whose stack trace is desired.
514 * @return String - The stack trace for the throwable instance.
515 */
516 public static final String stackTrace( Throwable throwable )
517 {
518 StringBuilder builder = new StringBuilder( 1024 );
519
520 builder.append( throwable.getMessage() );
521 builder.append( endOfLine );
522 printTrace( throwable, builder );
523
524 // Loop through the causes if they exist
525 Throwable throwable1 = throwable.getCause();
526 while ( throwable1 != null )
527 {
528 builder.append( "Caused by: " );
529 builder.append( throwable1.toString() );
530 builder.append( endOfLine );
531 printTrace( throwable1, builder );
532 throwable1 = throwable1.getCause();
533 }
534
535 return builder.toString();
536 }
537
538 /**
539 * Replace XML/HTML special characters (&, <, ...) in the
540 * String specified with their escaped equivalents.
541 *
542 * @param content - The <code>String</code> that
543 * is to be processed.
544 * @return String - The modified <code>String</code> object.
545 */
546 public static final String toXML( String content )
547 {
548 if ( content == null ) return content;
549
550 char contentChars[] = content.toCharArray();
551 StringBuilder builder = new StringBuilder( contentChars.length + 256 );
552 for ( int i = 0; i < contentChars.length; ++i )
553 {
554 switch( contentChars[i] )
555 {
556 case '&':
557 // Check to see if the ampersand has already been escaped
558 if ( i + 4 < contentChars.length )
559 {
560 if ( contentChars[i + 1] == 'a' &&
561 contentChars[i + 2] == 'm' &&
562 contentChars[i + 3] == 'p' &&
563 contentChars[i + 4] == ';' )
564 {
565 builder.append( contentChars[i] );
566 }
567 else
568 {
569 builder.append( "&" );
570 }
571 }
572 else
573 {
574 builder.append( "&" );
575 }
576 break;
577 case '<':
578 builder.append("<");
579 break;
580 case '>':
581 builder.append(">");
582 break;
583 case '\'':
584 case '`':
585 builder.append("'");
586 break;
587 case '"':
588 builder.append(""");
589 break;
590 case '\\':
591 builder.append("\");
592 break;
593 case (char)133:
594 builder.append("…");
595 break;
596 default:
597 builder.append( contentChars[i] );
598 break;
599 }
600 }
601
602 return builder.toString();
603 }
604
605 /**
606 * Replace escaped XML/HTML special characters (&, <, ...) in
607 * the String specified with their original equivalents.
608 *
609 * @since version 1.16
610 * @param content - The <code>String</code> that
611 * is to be processed.
612 * @return String - The modified <code>String</code> object.
613 */
614 public static final String fromXML( String content )
615 {
616 if ( content == null ) return content;
617
618 char contentChars[] = content.toCharArray();
619 StringBuilder builder = new StringBuilder( contentChars.length );
620 for ( int i = 0; i < contentChars.length; ++i )
621 {
622 boolean processed = false;
623 if ( contentChars[i] == '&' )
624 {
625 // Check for escaped ampersand and backslash
626 if ( i + 5 < contentChars.length )
627 {
628 // Check for escaped apostrophe and quote and 133
629 if ( contentChars[i + 1] == 'a' &&
630 contentChars[i + 2] == 'p' &&
631 contentChars[i + 3] == 'o' &&
632 contentChars[i + 4] == 's' &&
633 contentChars[i + 5] == ';' )
634 {
635 builder.append( '\'' );
636 processed = true;
637 i += 5;
638 }
639 else if ( contentChars[i + 1] == 'q' &&
640 contentChars[i + 2] == 'u' &&
641 contentChars[i + 3] == 'o' &&
642 contentChars[i + 4] == 't' &&
643 contentChars[i + 5] == ';' )
644 {
645 builder.append( '"' );
646 processed = true;
647 i += 5;
648 }
649 else if ( contentChars[i + 1] == '#' &&
650 contentChars[i + 2] == '1' &&
651 contentChars[i + 3] == '3' &&
652 contentChars[i + 4] == '3' &&
653 contentChars[i + 5] == ';' )
654 {
655 builder.append( (char) 133 );
656 processed = true;
657 i += 5;
658 }
659 }
660 if ( i + 4 < contentChars.length && ! processed )
661 {
662 if ( contentChars[i + 1] == 'a' &&
663 contentChars[i + 2] == 'm' &&
664 contentChars[i + 3] == 'p' &&
665 contentChars[i + 4] == ';' )
666 {
667 builder.append( contentChars[i] );
668 processed = true;
669 i += 4;
670 }
671 else if ( contentChars[i + 1] == '#' &&
672 contentChars[i + 2] == '9' &&
673 contentChars[i + 3] == '2' &&
674 contentChars[i + 4] == ';' )
675 {
676 builder.append( '\\' );
677 processed = true;
678 i += 4;
679 }
680 }
681 if ( i + 3 < contentChars.length && ! processed )
682 {
683 // Check for escaped less than or greater than
684 if ( i + 3 < contentChars.length )
685 {
686 if ( contentChars[i + 1] == 'l' &&
687 contentChars[i + 2] == 't' &&
688 contentChars[i + 3] == ';' )
689 {
690 builder.append( '<' );
691 processed = true;
692 i += 3;
693 }
694 else if ( contentChars[i + 1] == 'g' &&
695 contentChars[i + 2] == 't' &&
696 contentChars[i + 3] == ';' )
697 {
698 builder.append( '>' );
699 processed = true;
700 i += 3;
701 }
702 }
703 }
704 if ( ! processed )
705 {
706 builder.append( contentChars[i] );
707 }
708 }
709 else
710 {
711 builder.append( contentChars[i] );
712 }
713 }
714
715 return builder.toString();
716 }
717
718 /**
719 * Read the response from the <code>HttpURLConnection</code>. Check
720 * the <code>Content-encoding</code> response header to see if the
721 * response is <code>gzip</code> encoded. If it is, use a
722 * <code>GZIPInputStream</code> to read the response.
723 *
724 * @param httpConnection The <code>HttpURLConnection</code> from
725 * which the response is to be read.
726 * @throws IOException If errors are encountered while reading the
727 * response from the server.
728 */
729 private static final String readResponseFromUrl(
730 HttpURLConnection httpConnection ) throws IOException
731 {
732 String result = null;
733 BufferedReader reader = null;
734 String encoding = parseEncoding( httpConnection.getContentType() );
735
736 boolean gzipped = false;
737 String value = httpConnection.getHeaderField( "Content-Encoding" );
738 if ( value == null )
739 {
740 value = httpConnection.getHeaderField( "Content-encoding" );
741 }
742 if ( value != null && value.equals( "gzip" ) )
743 {
744 gzipped = true;
745 }
746
747 if ( httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK )
748 {
749
750 if ( encoding == null )
751 {
752 if ( gzipped )
753 {
754 reader = new BufferedReader(
755 new InputStreamReader(
756 new GZIPInputStream(
757 httpConnection.getInputStream() ) ) );
758 }
759 else
760 {
761 reader = new BufferedReader(
762 new InputStreamReader(
763 httpConnection.getInputStream() ) );
764 }
765 }
766 else
767 {
768 if ( gzipped )
769 {
770 reader = new BufferedReader(
771 new InputStreamReader(
772 new GZIPInputStream( httpConnection.getInputStream() ),
773 encoding.toUpperCase() ) );
774 }
775 else
776 {
777 reader = new BufferedReader(
778 new InputStreamReader(
779 httpConnection.getInputStream(),
780 encoding.toUpperCase() ) );
781 }
782 }
783
784 result = readData( reader );
785 reader.close();
786 }
787 else
788 {
789 InputStream inputStream = httpConnection.getErrorStream();
790 if ( inputStream != null )
791 {
792 if ( gzipped )
793 {
794 reader = new BufferedReader(
795 new InputStreamReader(
796 new GZIPInputStream( inputStream ) ) );
797 }
798 else
799 {
800 reader = new BufferedReader(
801 new InputStreamReader( inputStream ) );
802 }
803 String error = readData( reader );
804 reader.close();
805 throw new IOException( error );
806 }
807 else
808 {
809 throw new IOException( "HTTP Error " + httpConnection.getResponseCode() );
810 }
811 }
812
813 return result;
814 }
815
816 /**
817 * Create a new instance of <code>Authenticator</code> using the
818 * specified credentials. Create an anonymous sub-class of
819 * <code>Authenticator</code> that implements the <code>
820 * getPasswordAuthentication</code> method by returning a new
821 * instance of <code>PasswordAuthentication</code> using the
822 * <code>userName</code> and <code>password</code> specified.
823 *
824 * @param userName The user name to use for authentication.
825 * @param password The password to use for authentication.
826 */
827 private static final void setAuthenticator( final String userName,
828 final String password )
829 {
830 Authenticator.setDefault(
831 new Authenticator()
832 {
833 protected PasswordAuthentication getPasswordAuthentication()
834 {
835 return new PasswordAuthentication(
836 userName, password.toCharArray() );
837 }
838 }
839 );
840 }
841
842 /**
843 * Returns all the data from the specified <code>BufferedReader</code>.
844 *
845 * @param reader The <code>BufferedReader</code> from which the
846 * data is to be read.
847 * @return String - The data read from the specified BufferedReader.
848 * @throws IOException If errors are encountered while reading the
849 * data.
850 */
851 private static String readData( BufferedReader reader )
852 throws IOException
853 {
854 StringBuilder builder = new StringBuilder( 8192 );
855 String line = null;
856 while ( ( line = reader.readLine() ) != null )
857 {
858 builder.append( line );
859 builder.append( endOfLine );
860 }
861
862 return builder.toString();
863 }
864
865 /**
866 * Return the <code>stack trace</code> for the specified instance
867 * of <code>Throwable</code>.
868 *
869 * @param throwable The instance of <code>Throwable</code>
870 * whose <code>stack trace</code> is to be retrieved.
871 * @param builder The StringBuilder to which the stack trace is to
872 * be appended.
873 * @return StringBuilder - The reference to the StringBuilder that
874 * was passed in for convenience.
875 */
876 private static final StringBuilder printTrace( Throwable throwable,
877 StringBuilder builder )
878 {
879 StackTraceElement [] ste = throwable.getStackTrace();
880
881 for ( int i = 0; i < ste.length; ++i )
882 {
883 builder.append( "\tat " );
884 builder.append( ste[i].getClassName() );
885 builder.append( "." );
886 builder.append( ste[i].getMethodName() );
887 builder.append( "(" );
888 builder.append( ste[i].getFileName() );
889 builder.append( ":" );
890 builder.append( ste[i].getLineNumber() );
891 builder.append( ")" );
892 builder.append( endOfLine );
893 }
894
895 return builder;
896 }
897 }