1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.james.util.io;
19
20 import java.io.BufferedInputStream;
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.OutputStream;
27 import java.io.OutputStreamWriter;
28 import java.io.Reader;
29 import java.io.StringReader;
30 import java.io.StringWriter;
31 import java.io.Writer;
32
33 /***
34 * General IO Stream manipulation.
35 * <p>
36 * This class provides static utility methods for input/output operations, particularly buffered
37 * copying between sources (<code>InputStream</code>, <code>Reader</code>, <code>String</code> and
38 * <code>byte[]</code>) and destinations (<code>OutputStream</code>, <code>Writer</code>,
39 * <code>String</code> and <code>byte[]</code>).
40 * </p>
41 *
42 * <p>Unless otherwise noted, these <code>copy</code> methods do <em>not</em> flush or close the
43 * streams. Often, doing so would require making non-portable assumptions about the streams' origin
44 * and further use. This means that both streams' <code>close()</code> methods must be called after
45 * copying. if one omits this step, then the stream resources (sockets, file descriptors) are
46 * released when the associated Stream is garbage-collected. It is not a good idea to rely on this
47 * mechanism. For a good overview of the distinction between "memory management" and "resource
48 * management", see <a href="http://www.unixreview.com/articles/1998/9804/9804ja/ja.htm">this
49 * UnixReview article</a></p>
50 *
51 * <p>For each <code>copy</code> method, a variant is provided that allows the caller to specify the
52 * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this
53 * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data
54 * transfers.</p>
55 *
56 * <p>For byte-to-char methods, a <code>copy</code> variant allows the encoding to be selected
57 * (otherwise the platform default is used).</p>
58 *
59 * <p>The <code>copy</code> methods use an internal buffer when copying. It is therefore advisable
60 * <em>not</em> to deliberately wrap the stream arguments to the <code>copy</code> methods in
61 * <code>Buffered*</code> streams. For example, don't do the
62 * following:</p>
63 *
64 * <code>copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );</code>
65 *
66 * <p>The rationale is as follows:</p>
67 *
68 * <p>Imagine that an InputStream's read() is a very expensive operation, which would usually suggest
69 * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent
70 * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to
71 * fill an internal buffer, from which further <code>read</code> requests can inexpensively get
72 * their data (until the buffer runs out).</p>
73 * <p>However, the <code>copy</code> methods do the same thing, keeping an internal buffer,
74 * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers
75 * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer
76 * management hurts performance slightly (about 3%, according to some simple experiments).</p>
77 *
78 * @author Peter Donald
79 * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
80 * @version CVS $Revision: 365582 $ $Date: 2006-01-03 08:51:21 +0000 (mar, 03 gen 2006) $
81 * @since 4.0
82 */
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public final class IOUtil
116 {
117 private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
118
119 /***
120 * Private constructor to prevent instantiation.
121 */
122 private IOUtil()
123 {
124 }
125
126 /***
127 * Unconditionally close an <code>Reader</code>.
128 * Equivalent to {@link Reader#close()}, except any exceptions will be ignored.
129 *
130 * @param input A (possibly null) Reader
131 */
132 public static void shutdownReader( final Reader input )
133 {
134 if( null == input )
135 {
136 return;
137 }
138
139 try
140 {
141 input.close();
142 }
143 catch( final IOException ioe )
144 {
145 }
146 }
147
148 /***
149 * Unconditionally close an <code>Writer</code>.
150 * Equivalent to {@link Writer#close()}, except any exceptions will be ignored.
151 *
152 * @param output A (possibly null) Writer
153 */
154 public static void shutdownWriter( final Writer output )
155 {
156 if( null == output )
157 {
158 return;
159 }
160
161 try
162 {
163 output.close();
164 }
165 catch( final IOException ioe )
166 {
167 }
168 }
169
170 /***
171 * Unconditionally close an <code>OutputStream</code>.
172 * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored.
173 * @param output A (possibly null) OutputStream
174 */
175 public static void shutdownStream( final OutputStream output )
176 {
177 if( null == output )
178 {
179 return;
180 }
181
182 try
183 {
184 output.close();
185 }
186 catch( final IOException ioe )
187 {
188 }
189 }
190
191 /***
192 * Unconditionally close an <code>InputStream</code>.
193 * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored.
194 * @param input A (possibly null) InputStream
195 */
196 public static void shutdownStream( final InputStream input )
197 {
198 if( null == input )
199 {
200 return;
201 }
202
203 try
204 {
205 input.close();
206 }
207 catch( final IOException ioe )
208 {
209 }
210 }
211
212
213
214
215
216 /***
217 * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
218 */
219 public static void copy( final InputStream input, final OutputStream output )
220 throws IOException
221 {
222 copy( input, output, DEFAULT_BUFFER_SIZE );
223 }
224
225 /***
226 * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
227 * @param bufferSize Size of internal buffer to use.
228 */
229 public static void copy( final InputStream input,
230 final OutputStream output,
231 final int bufferSize )
232 throws IOException
233 {
234 final byte[] buffer = new byte[ bufferSize ];
235 int n = 0;
236 while( -1 != ( n = input.read( buffer ) ) )
237 {
238 output.write( buffer, 0, n );
239 }
240 }
241
242 /***
243 * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
244 */
245 public static void copy( final Reader input, final Writer output )
246 throws IOException
247 {
248 copy( input, output, DEFAULT_BUFFER_SIZE );
249 }
250
251 /***
252 * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
253 * @param bufferSize Size of internal buffer to use.
254 */
255 public static void copy( final Reader input, final Writer output, final int bufferSize )
256 throws IOException
257 {
258 final char[] buffer = new char[ bufferSize ];
259 int n = 0;
260 while( -1 != ( n = input.read( buffer ) ) )
261 {
262 output.write( buffer, 0, n );
263 }
264 }
265
266
267
268
269
270
271
272
273
274
275 /***
276 * Copy and convert bytes from an <code>InputStream</code> to chars on a
277 * <code>Writer</code>.
278 * The platform's default encoding is used for the byte-to-char conversion.
279 */
280 public static void copy( final InputStream input, final Writer output )
281 throws IOException
282 {
283 copy( input, output, DEFAULT_BUFFER_SIZE );
284 }
285
286 /***
287 * Copy and convert bytes from an <code>InputStream</code> to chars on a
288 * <code>Writer</code>.
289 * The platform's default encoding is used for the byte-to-char conversion.
290 * @param bufferSize Size of internal buffer to use.
291 */
292 public static void copy( final InputStream input, final Writer output, final int bufferSize )
293 throws IOException
294 {
295 final InputStreamReader in = new InputStreamReader( input );
296 copy( in, output, bufferSize );
297 }
298
299 /***
300 * Copy and convert bytes from an <code>InputStream</code> to chars on a
301 * <code>Writer</code>, using the specified encoding.
302 * @param encoding The name of a supported character encoding. See the
303 * <a href="http://www.iana.org/assignments/character-sets">IANA
304 * Charset Registry</a> for a list of valid encoding types.
305 */
306 public static void copy( final InputStream input, final Writer output, final String encoding )
307 throws IOException
308 {
309 final InputStreamReader in = new InputStreamReader( input, encoding );
310 copy( in, output );
311 }
312
313 /***
314 * Copy and convert bytes from an <code>InputStream</code> to chars on a
315 * <code>Writer</code>, using the specified encoding.
316 * @param encoding The name of a supported character encoding. See the
317 * <a href="http://www.iana.org/assignments/character-sets">IANA
318 * Charset Registry</a> for a list of valid encoding types.
319 * @param bufferSize Size of internal buffer to use.
320 */
321 public static void copy( final InputStream input,
322 final Writer output,
323 final String encoding,
324 final int bufferSize )
325 throws IOException
326 {
327 final InputStreamReader in = new InputStreamReader( input, encoding );
328 copy( in, output, bufferSize );
329 }
330
331
332
333
334
335 /***
336 * Get the contents of an <code>InputStream</code> as a String.
337 * The platform's default encoding is used for the byte-to-char conversion.
338 */
339 public static String toString( final InputStream input )
340 throws IOException
341 {
342 return toString( input, DEFAULT_BUFFER_SIZE );
343 }
344
345 /***
346 * Get the contents of an <code>InputStream</code> as a String.
347 * The platform's default encoding is used for the byte-to-char conversion.
348 * @param bufferSize Size of internal buffer to use.
349 */
350 public static String toString( final InputStream input, final int bufferSize )
351 throws IOException
352 {
353 final StringWriter sw = new StringWriter();
354 copy( input, sw, bufferSize );
355 return sw.toString();
356 }
357
358 /***
359 * Get the contents of an <code>InputStream</code> as a String.
360 * @param encoding The name of a supported character encoding. See the
361 * <a href="http://www.iana.org/assignments/character-sets">IANA
362 * Charset Registry</a> for a list of valid encoding types.
363 */
364 public static String toString( final InputStream input, final String encoding )
365 throws IOException
366 {
367 return toString( input, encoding, DEFAULT_BUFFER_SIZE );
368 }
369
370 /***
371 * Get the contents of an <code>InputStream</code> as a String.
372 * @param encoding The name of a supported character encoding. See the
373 * <a href="http://www.iana.org/assignments/character-sets">IANA
374 * Charset Registry</a> for a list of valid encoding types.
375 * @param bufferSize Size of internal buffer to use.
376 */
377 public static String toString( final InputStream input,
378 final String encoding,
379 final int bufferSize )
380 throws IOException
381 {
382 final StringWriter sw = new StringWriter();
383 copy( input, sw, encoding, bufferSize );
384 return sw.toString();
385 }
386
387
388
389
390 /***
391 * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
392 */
393 public static byte[] toByteArray( final InputStream input )
394 throws IOException
395 {
396 return toByteArray( input, DEFAULT_BUFFER_SIZE );
397 }
398
399 /***
400 * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
401 * @param bufferSize Size of internal buffer to use.
402 */
403 public static byte[] toByteArray( final InputStream input, final int bufferSize )
404 throws IOException
405 {
406 final ByteArrayOutputStream output = new ByteArrayOutputStream();
407 copy( input, output, bufferSize );
408 return output.toByteArray();
409 }
410
411
412
413
414
415
416
417
418
419 /***
420 * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
421 * flush the <code>OutputStream</code>.
422 */
423 public static void copy( final Reader input, final OutputStream output )
424 throws IOException
425 {
426 copy( input, output, DEFAULT_BUFFER_SIZE );
427 }
428
429 /***
430 * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
431 * flush the <code>OutputStream</code>.
432 * @param bufferSize Size of internal buffer to use.
433 */
434 public static void copy( final Reader input, final OutputStream output, final int bufferSize )
435 throws IOException
436 {
437 final OutputStreamWriter out = new OutputStreamWriter( output );
438 copy( input, out, bufferSize );
439
440
441 out.flush();
442 }
443
444
445
446 /***
447 * Get the contents of a <code>Reader</code> as a String.
448 */
449 public static String toString( final Reader input )
450 throws IOException
451 {
452 return toString( input, DEFAULT_BUFFER_SIZE );
453 }
454
455 /***
456 * Get the contents of a <code>Reader</code> as a String.
457 * @param bufferSize Size of internal buffer to use.
458 */
459 public static String toString( final Reader input, final int bufferSize )
460 throws IOException
461 {
462 final StringWriter sw = new StringWriter();
463 copy( input, sw, bufferSize );
464 return sw.toString();
465 }
466
467
468
469
470 /***
471 * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
472 */
473 public static byte[] toByteArray( final Reader input )
474 throws IOException
475 {
476 return toByteArray( input, DEFAULT_BUFFER_SIZE );
477 }
478
479 /***
480 * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
481 * @param bufferSize Size of internal buffer to use.
482 */
483 public static byte[] toByteArray( final Reader input, final int bufferSize )
484 throws IOException
485 {
486 ByteArrayOutputStream output = new ByteArrayOutputStream();
487 copy( input, output, bufferSize );
488 return output.toByteArray();
489 }
490
491
492
493
494
495
496
497
498
499
500
501 /***
502 * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
503 * flush the <code>OutputStream</code>.
504 */
505 public static void copy( final String input, final OutputStream output )
506 throws IOException
507 {
508 copy( input, output, DEFAULT_BUFFER_SIZE );
509 }
510
511 /***
512 * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
513 * flush the <code>OutputStream</code>.
514 * @param bufferSize Size of internal buffer to use.
515 */
516 public static void copy( final String input, final OutputStream output, final int bufferSize )
517 throws IOException
518 {
519 final StringReader in = new StringReader( input );
520 final OutputStreamWriter out = new OutputStreamWriter( output );
521 copy( in, out, bufferSize );
522
523
524 out.flush();
525 }
526
527
528
529
530
531
532 /***
533 * Copy chars from a <code>String</code> to a <code>Writer</code>.
534 */
535 public static void copy( final String input, final Writer output )
536 throws IOException
537 {
538 output.write( input );
539 }
540
541
542
543 /***
544 * Get the contents of a <code>String</code> as a <code>byte[]</code>.
545 */
546 public static byte[] toByteArray( final String input )
547 throws IOException
548 {
549 return toByteArray( input, DEFAULT_BUFFER_SIZE );
550 }
551
552 /***
553 * Get the contents of a <code>String</code> as a <code>byte[]</code>.
554 * @param bufferSize Size of internal buffer to use.
555 */
556 public static byte[] toByteArray( final String input, final int bufferSize )
557 throws IOException
558 {
559 ByteArrayOutputStream output = new ByteArrayOutputStream();
560 copy( input, output, bufferSize );
561 return output.toByteArray();
562 }
563
564
565
566
567
568
569
570
571
572
573
574
575 /***
576 * Copy and convert bytes from a <code>byte[]</code> to chars on a
577 * <code>Writer</code>.
578 * The platform's default encoding is used for the byte-to-char conversion.
579 */
580 public static void copy( final byte[] input, final Writer output )
581 throws IOException
582 {
583 copy( input, output, DEFAULT_BUFFER_SIZE );
584 }
585
586 /***
587 * Copy and convert bytes from a <code>byte[]</code> to chars on a
588 * <code>Writer</code>.
589 * The platform's default encoding is used for the byte-to-char conversion.
590 * @param bufferSize Size of internal buffer to use.
591 */
592 public static void copy( final byte[] input, final Writer output, final int bufferSize )
593 throws IOException
594 {
595 final ByteArrayInputStream in = new ByteArrayInputStream( input );
596 copy( in, output, bufferSize );
597 }
598
599 /***
600 * Copy and convert bytes from a <code>byte[]</code> to chars on a
601 * <code>Writer</code>, using the specified encoding.
602 * @param encoding The name of a supported character encoding. See the
603 * <a href="http://www.iana.org/assignments/character-sets">IANA
604 * Charset Registry</a> for a list of valid encoding types.
605 */
606 public static void copy( final byte[] input, final Writer output, final String encoding )
607 throws IOException
608 {
609 final ByteArrayInputStream in = new ByteArrayInputStream( input );
610 copy( in, output, encoding );
611 }
612
613 /***
614 * Copy and convert bytes from a <code>byte[]</code> to chars on a
615 * <code>Writer</code>, using the specified encoding.
616 * @param encoding The name of a supported character encoding. See the
617 * <a href="http://www.iana.org/assignments/character-sets">IANA
618 * Charset Registry</a> for a list of valid encoding types.
619 * @param bufferSize Size of internal buffer to use.
620 */
621 public static void copy( final byte[] input,
622 final Writer output,
623 final String encoding,
624 final int bufferSize )
625 throws IOException
626 {
627 final ByteArrayInputStream in = new ByteArrayInputStream( input );
628 copy( in, output, encoding, bufferSize );
629 }
630
631
632
633
634
635 /***
636 * Get the contents of a <code>byte[]</code> as a String.
637 * The platform's default encoding is used for the byte-to-char conversion.
638 */
639 public static String toString( final byte[] input )
640 throws IOException
641 {
642 return toString( input, DEFAULT_BUFFER_SIZE );
643 }
644
645 /***
646 * Get the contents of a <code>byte[]</code> as a String.
647 * The platform's default encoding is used for the byte-to-char conversion.
648 * @param bufferSize Size of internal buffer to use.
649 */
650 public static String toString( final byte[] input, final int bufferSize )
651 throws IOException
652 {
653 final StringWriter sw = new StringWriter();
654 copy( input, sw, bufferSize );
655 return sw.toString();
656 }
657
658 /***
659 * Get the contents of a <code>byte[]</code> as a String.
660 * @param encoding The name of a supported character encoding. See the
661 * <a href="http://www.iana.org/assignments/character-sets">IANA
662 * Charset Registry</a> for a list of valid encoding types.
663 */
664 public static String toString( final byte[] input, final String encoding )
665 throws IOException
666 {
667 return toString( input, encoding, DEFAULT_BUFFER_SIZE );
668 }
669
670 /***
671 * Get the contents of a <code>byte[]</code> as a String.
672 * @param encoding The name of a supported character encoding. See the
673 * <a href="http://www.iana.org/assignments/character-sets">IANA
674 * Charset Registry</a> for a list of valid encoding types.
675 * @param bufferSize Size of internal buffer to use.
676 */
677 public static String toString( final byte[] input,
678 final String encoding,
679 final int bufferSize )
680 throws IOException
681 {
682 final StringWriter sw = new StringWriter();
683 copy( input, sw, encoding, bufferSize );
684 return sw.toString();
685 }
686
687
688
689
690
691 /***
692 * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
693 */
694 public static void copy( final byte[] input, final OutputStream output )
695 throws IOException
696 {
697 copy( input, output, DEFAULT_BUFFER_SIZE );
698 }
699
700 /***
701 * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
702 * @param bufferSize Size of internal buffer to use.
703 */
704 public static void copy( final byte[] input,
705 final OutputStream output,
706 final int bufferSize )
707 throws IOException
708 {
709 output.write( input );
710 }
711
712 /***
713 * Compare the contents of two Streams to determine if they are equal or not.
714 *
715 * @param input1 the first stream
716 * @param input2 the second stream
717 * @return true if the content of the streams are equal or they both don't exist, false otherwise
718 */
719 public static boolean contentEquals( final InputStream input1,
720 final InputStream input2 )
721 throws IOException
722 {
723 final InputStream bufferedInput1 = new BufferedInputStream( input1 );
724 final InputStream bufferedInput2 = new BufferedInputStream( input2 );
725
726 int ch = bufferedInput1.read();
727 while( -1 != ch )
728 {
729 final int ch2 = bufferedInput2.read();
730 if( ch != ch2 )
731 {
732 return false;
733 }
734 ch = bufferedInput1.read();
735 }
736
737 final int ch2 = bufferedInput2.read();
738 if( -1 != ch2 )
739 {
740 return false;
741 }
742 else
743 {
744 return true;
745 }
746 }
747 }