View Javadoc

1   /* 
2    * Copyright 1999-2006 The Apache Software Foundation
3    * Licensed  under the  Apache License,  Version 2.0  (the "License");
4    * you may not use  this file  except in  compliance with the License.
5    * You may obtain a copy of the License at 
6    * 
7    *   http://www.apache.org/licenses/LICENSE-2.0
8    * 
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed  under the  License is distributed on an "AS IS" BASIS,
11   * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
12   * implied.
13   * 
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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 -&gt; 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   * Behold, intrepid explorers; a map of this class:
86   *
87   *       Method      Input               Output          Dependency
88   *       ------      -----               ------          -------
89   * 1     copy        InputStream         OutputStream    (primitive)
90   * 2     copy        Reader              Writer          (primitive)
91   *
92   * 3     copy        InputStream         Writer          2
93   * 4     toString    InputStream         String          3
94   * 5     toByteArray InputStream         byte[]          1
95   *
96   * 6     copy        Reader              OutputStream    2
97   * 7     toString    Reader              String          2
98   * 8     toByteArray Reader              byte[]          6
99   *
100  * 9     copy        String              OutputStream    2
101  * 10    copy        String              Writer          (trivial)
102  * 11    toByteArray String              byte[]          9
103  *
104  * 12    copy        byte[]              Writer          3
105  * 13    toString    byte[]              String          12
106  * 14    copy        byte[]              OutputStream    (trivial)
107  *
108  *
109  * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy
110  * using native Java copy methods. As there are method variants to specify buffer size and encoding,
111  * each row may correspond to up to 4 methods.
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     // Core copy methods
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     // Derived copy methods
268     // InputStream -> *
269     ///////////////////////////////////////////////////////////////
270 
271 
272     ///////////////////////////////////////////////////////////////
273     // InputStream -> Writer
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     // InputStream -> String
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     // InputStream -> byte[]
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     // Derived copy methods
414     // Reader -> *
415     ///////////////////////////////////////////////////////////////
416 
417     ///////////////////////////////////////////////////////////////
418     // Reader -> OutputStream
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         // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
440         // here.
441         out.flush();
442     }
443 
444     ///////////////////////////////////////////////////////////////
445     // Reader -> String
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     // Reader -> byte[]
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     // Derived copy methods
494     // String -> *
495     ///////////////////////////////////////////////////////////////
496 
497 
498     ///////////////////////////////////////////////////////////////
499     // String -> OutputStream
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         // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
523         // here.
524         out.flush();
525     }
526 
527 
528 
529     ///////////////////////////////////////////////////////////////
530     // String -> Writer
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     // String -> byte[]
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     // Derived copy methods
568     // byte[] -> *
569     ///////////////////////////////////////////////////////////////
570 
571 
572     ///////////////////////////////////////////////////////////////
573     // byte[] -> Writer
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     // byte[] -> String
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     // byte[] -> OutputStream
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 }