001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.util.io;
018
019import java.io.BufferedInputStream;
020import java.io.BufferedReader;
021import java.io.CharArrayWriter;
022import java.io.Closeable;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.InputStreamReader;
026import java.io.OutputStream;
027import java.io.OutputStreamWriter;
028import java.io.Reader;
029import java.io.StringWriter;
030import java.io.Writer;
031
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035/**
036 * General IO Stream manipulation.
037 * <p>
038 * This class provides static utility methods for input/output operations.
039 * <ul>
040 * <li>closeQuietly - these method closes any kind of closeable resource, e.g. an input/output
041 * stream or reader/writer ignoring nulls and exceptions
042 * <li>toXxx - these methods read data from a stream
043 * <li>write - these methods write data to a stream
044 * <li>copy - these methods copy all the data from one stream to another
045 * <li>contentEquals - these methods compare the content of two streams
046 * </ul>
047 * <p>
048 * The byte-to-char methods and char-to-byte methods involve a conversion step. Two methods are
049 * provided in each case, one that uses the platform default encoding and the other which allows you
050 * to specify an encoding. You are encouraged to always specify an encoding because relying on the
051 * platform default can lead to unexpected results, for example when moving from development to
052 * production.
053 * <p>
054 * All the methods in this class that read a stream are buffered internally. This means that there
055 * is no cause to use a <code>BufferedInputStream</code> or <code>BufferedReader</code>. The default
056 * buffer size of 4K has been show to be efficient in tests.
057 * <p>
058 * Wherever possible, the methods in this class do <em>not</em> flush or close the stream. This is
059 * to avoid making non-portable assumptions about the streams' origin and further use. Thus the
060 * caller is still responsible for closing streams after use.
061 * <p>
062 * Origin of code: Apache Avalon (Excalibur)
063 * 
064 * @author Peter Donald
065 * @author Jeff Turner
066 * @author Matthew Hawthorne
067 * @author Stephen Colebourne
068 * @author Gareth Davis
069 */
070public final class IOUtils
071{
072        private static final Logger log = LoggerFactory.getLogger(IOUtils.class);
073
074        // NOTE: This class is focused on InputStream, OutputStream, Reader and
075        // Writer. Each method should take at least one of these as a parameter.
076        // NOTE: This class should not depend on any other classes
077
078        /**
079         * The default buffer size to use.
080         */
081        private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
082
083        /**
084         * Instances should NOT be constructed in standard programming.
085         */
086        public IOUtils()
087        {
088        }
089
090        /**
091         * Closes a closeable. Guards against null closables.
092         * 
093         * @param closeable
094         *            closeable to close
095         * @throws IOException
096         *             when close fails
097         */
098        public static void close(final Closeable closeable) throws IOException
099        {
100                if (closeable != null)
101                {
102                        closeable.close();
103                }
104        }
105
106        /**
107         * Unconditionally close a <code>Closeable</code>.
108         * <p>
109         * closeables can be input or output streams, reader, writers, and much more.
110         * 
111         * Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. This is
112         * typically used in finally blocks.
113         * 
114         * @param closeable
115         *            the Closeable to close, may be null or already closed
116         */
117        public static void closeQuietly(final Closeable closeable)
118        {
119                try
120                {
121                        close(closeable);
122                }
123                catch (IOException e)
124                {
125                        log.debug("closing resource failed: " + e.getMessage(), e);
126                }
127        }
128
129        /**
130         * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
131         * <p>
132         * This method buffers the input internally, so there is no need to use a
133         * <code>BufferedInputStream</code>.
134         * 
135         * @param input
136         *            the <code>InputStream</code> to read from
137         * @return the requested byte array
138         * @throws NullPointerException
139         *             if the input is null
140         * @throws IOException
141         *             if an I/O error occurs
142         */
143        public static byte[] toByteArray(final InputStream input) throws IOException
144        {
145                ByteArrayOutputStream output = new ByteArrayOutputStream();
146                copy(input, output);
147                return output.toByteArray();
148        }
149
150        /**
151         * Get the contents of a <code>Reader</code> as a <code>byte[]</code> using the default
152         * character encoding of the platform.
153         * <p>
154         * This method buffers the input internally, so there is no need to use a
155         * <code>BufferedReader</code>.
156         * 
157         * @param input
158         *            the <code>Reader</code> to read from
159         * @return the requested byte array
160         * @throws NullPointerException
161         *             if the input is null
162         * @throws IOException
163         *             if an I/O error occurs
164         */
165        public static byte[] toByteArray(final Reader input) throws IOException
166        {
167                ByteArrayOutputStream output = new ByteArrayOutputStream();
168                copy(input, output);
169                return output.toByteArray();
170        }
171
172        /**
173         * Get the contents of a <code>Reader</code> as a <code>byte[]</code> using the specified
174         * character encoding.
175         * <p>
176         * Character encoding names can be found at <a
177         * href="http://www.iana.org/assignments/character-sets">IANA</a>.
178         * <p>
179         * This method buffers the input internally, so there is no need to use a
180         * <code>BufferedReader</code>.
181         * 
182         * @param input
183         *            the <code>Reader</code> to read from
184         * @param encoding
185         *            the encoding to use, null means platform default
186         * @return the requested byte array
187         * @throws NullPointerException
188         *             if the input is null
189         * @throws IOException
190         *             if an I/O error occurs
191         * @since 1.1
192         */
193        public static byte[] toByteArray(final Reader input, final String encoding) throws IOException
194        {
195                ByteArrayOutputStream output = new ByteArrayOutputStream();
196                copy(input, output, encoding);
197                return output.toByteArray();
198        }
199
200        /**
201         * Get the contents of an <code>InputStream</code> as a character array using the default
202         * character encoding of the platform.
203         * <p>
204         * This method buffers the input internally, so there is no need to use a
205         * <code>BufferedInputStream</code>.
206         * 
207         * @param is
208         *            the <code>InputStream</code> to read from
209         * @return the requested character array
210         * @throws NullPointerException
211         *             if the input is null
212         * @throws IOException
213         *             if an I/O error occurs
214         */
215        public static char[] toCharArray(final InputStream is) throws IOException
216        {
217                CharArrayWriter output = new CharArrayWriter();
218                copy(is, output);
219                return output.toCharArray();
220        }
221
222        /**
223         * Get the contents of an <code>InputStream</code> as a character array using the specified
224         * character encoding.
225         * <p>
226         * Character encoding names can be found at <a
227         * href="http://www.iana.org/assignments/character-sets">IANA</a>.
228         * <p>
229         * This method buffers the input internally, so there is no need to use a
230         * <code>BufferedInputStream</code>.
231         * 
232         * @param is
233         *            the <code>InputStream</code> to read from
234         * @param encoding
235         *            the encoding to use, null means platform default
236         * @return the requested character array
237         * @throws NullPointerException
238         *             if the input is null
239         * @throws IOException
240         *             if an I/O error occurs
241         */
242        public static char[] toCharArray(final InputStream is, final String encoding)
243                throws IOException
244        {
245                CharArrayWriter output = new CharArrayWriter();
246                copy(is, output, encoding);
247                return output.toCharArray();
248        }
249
250        /**
251         * Get the contents of a <code>Reader</code> as a character array.
252         * <p>
253         * This method buffers the input internally, so there is no need to use a
254         * <code>BufferedReader</code>.
255         * 
256         * @param input
257         *            the <code>Reader</code> to read from
258         * @return the requested character array
259         * @throws NullPointerException
260         *             if the input is null
261         * @throws IOException
262         *             if an I/O error occurs
263         */
264        public static char[] toCharArray(final Reader input) throws IOException
265        {
266                CharArrayWriter sw = new CharArrayWriter();
267                copy(input, sw);
268                return sw.toCharArray();
269        }
270
271        /**
272         * Get the contents of an <code>InputStream</code> as a String using the default character
273         * encoding of the platform.
274         * <p>
275         * This method buffers the input internally, so there is no need to use a
276         * <code>BufferedInputStream</code>.
277         * 
278         * @param input
279         *            the <code>InputStream</code> to read from
280         * @return the requested String
281         * @throws NullPointerException
282         *             if the input is null
283         * @throws IOException
284         *             if an I/O error occurs
285         */
286        public static String toString(final InputStream input) throws IOException
287        {
288                StringWriter sw = new StringWriter();
289                copy(input, sw);
290                return sw.toString();
291        }
292
293        /**
294         * Get the contents of an <code>InputStream</code> as a String using the specified character
295         * encoding.
296         * <p>
297         * Character encoding names can be found at <a
298         * href="http://www.iana.org/assignments/character-sets">IANA</a>.
299         * <p>
300         * This method buffers the input internally, so there is no need to use a
301         * <code>BufferedInputStream</code>.
302         * 
303         * @param input
304         *            the <code>InputStream</code> to read from
305         * @param encoding
306         *            the encoding to use, null means platform default
307         * @return the requested String
308         * @throws NullPointerException
309         *             if the input is null
310         * @throws IOException
311         *             if an I/O error occurs
312         */
313        public static String toString(final InputStream input, final String encoding)
314                throws IOException
315        {
316                StringWriter sw = new StringWriter();
317                copy(input, sw, encoding);
318                return sw.toString();
319        }
320
321        /**
322         * Get the contents of a <code>Reader</code> as a String.
323         * <p>
324         * This method buffers the input internally, so there is no need to use a
325         * <code>BufferedReader</code>.
326         * 
327         * @param input
328         *            the <code>Reader</code> to read from
329         * @return the requested String
330         * @throws NullPointerException
331         *             if the input is null
332         * @throws IOException
333         *             if an I/O error occurs
334         */
335        public static String toString(final Reader input) throws IOException
336        {
337                StringWriter sw = new StringWriter();
338                copy(input, sw);
339                return sw.toString();
340        }
341
342        /**
343         * Writes bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
344         * 
345         * @param data
346         *            the byte array to write, do not modify during output, null ignored
347         * @param output
348         *            the <code>OutputStream</code> to write to
349         * @throws NullPointerException
350         *             if output is null
351         * @throws IOException
352         *             if an I/O error occurs
353         * @since 1.1
354         */
355        public static void write(final byte[] data, final OutputStream output) throws IOException
356        {
357                if (data != null)
358                {
359                        output.write(data);
360                }
361        }
362
363        /**
364         * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code> using the default
365         * character encoding of the platform.
366         * <p>
367         * This method uses {@link String#String(byte[])}.
368         * 
369         * @param data
370         *            the byte array to write, do not modify during output, null ignored
371         * @param output
372         *            the <code>Writer</code> to write to
373         * @throws NullPointerException
374         *             if output is null
375         * @throws IOException
376         *             if an I/O error occurs
377         * @since 1.1
378         */
379        public static void write(final byte[] data, final Writer output) throws IOException
380        {
381                if (data != null)
382                {
383                        output.write(new String(data));
384                }
385        }
386
387        /**
388         * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code> using the specified
389         * character encoding.
390         * <p>
391         * Character encoding names can be found at <a
392         * href="http://www.iana.org/assignments/character-sets">IANA</a>.
393         * <p>
394         * This method uses {@link String#String(byte[], String)}.
395         * 
396         * @param data
397         *            the byte array to write, do not modify during output, null ignored
398         * @param output
399         *            the <code>Writer</code> to write to
400         * @param encoding
401         *            the encoding to use, null means platform default
402         * @throws NullPointerException
403         *             if output is null
404         * @throws IOException
405         *             if an I/O error occurs
406         * @since 1.1
407         */
408        public static void write(final byte[] data, final Writer output, final String encoding)
409                throws IOException
410        {
411                if (data != null)
412                {
413                        if (encoding == null)
414                        {
415                                write(data, output);
416                        }
417                        else
418                        {
419                                output.write(new String(data, encoding));
420                        }
421                }
422        }
423
424        /**
425         * Writes chars from a <code>char[]</code> to a <code>Writer</code> using the default character
426         * encoding of the platform.
427         * 
428         * @param data
429         *            the char array to write, do not modify during output, null ignored
430         * @param output
431         *            the <code>Writer</code> to write to
432         * @throws NullPointerException
433         *             if output is null
434         * @throws IOException
435         *             if an I/O error occurs
436         * @since 1.1
437         */
438        public static void write(final char[] data, final Writer output) throws IOException
439        {
440                if (data != null)
441                {
442                        output.write(data);
443                }
444        }
445
446        /**
447         * Writes chars from a <code>char[]</code> to bytes on an <code>OutputStream</code>.
448         * <p>
449         * This method uses {@link String#String(char[])} and {@link String#getBytes()}.
450         * 
451         * @param data
452         *            the char array to write, do not modify during output, null ignored
453         * @param output
454         *            the <code>OutputStream</code> to write to
455         * @throws NullPointerException
456         *             if output is null
457         * @throws IOException
458         *             if an I/O error occurs
459         * @since 1.1
460         */
461        public static void write(final char[] data, final OutputStream output) throws IOException
462        {
463                if (data != null)
464                {
465                        output.write(new String(data).getBytes());
466                }
467        }
468
469        /**
470         * Writes chars from a <code>char[]</code> to bytes on an <code>OutputStream</code> using the
471         * specified character encoding.
472         * <p>
473         * Character encoding names can be found at <a
474         * href="http://www.iana.org/assignments/character-sets">IANA</a>.
475         * <p>
476         * This method uses {@link String#String(char[])} and {@link String#getBytes(String)}.
477         * 
478         * @param data
479         *            the char array to write, do not modify during output, null ignored
480         * @param output
481         *            the <code>OutputStream</code> to write to
482         * @param encoding
483         *            the encoding to use, null means platform default
484         * @throws NullPointerException
485         *             if output is null
486         * @throws IOException
487         *             if an I/O error occurs
488         * @since 1.1
489         */
490        public static void write(final char[] data, final OutputStream output, final String encoding)
491                throws IOException
492        {
493                if (data != null)
494                {
495                        if (encoding == null)
496                        {
497                                write(data, output);
498                        }
499                        else
500                        {
501                                output.write(new String(data).getBytes(encoding));
502                        }
503                }
504        }
505
506        /**
507         * Writes chars from a <code>String</code> to a <code>Writer</code>.
508         * 
509         * @param data
510         *            the <code>String</code> to write, null ignored
511         * @param output
512         *            the <code>Writer</code> to write to
513         * @throws NullPointerException
514         *             if output is null
515         * @throws IOException
516         *             if an I/O error occurs
517         * @since 1.1
518         */
519        public static void write(final String data, final Writer output) throws IOException
520        {
521                if (data != null)
522                {
523                        output.write(data);
524                }
525        }
526
527        /**
528         * Writes chars from a <code>String</code> to bytes on an <code>OutputStream</code> using the
529         * default character encoding of the platform.
530         * <p>
531         * This method uses {@link String#getBytes()}.
532         * 
533         * @param data
534         *            the <code>String</code> to write, null ignored
535         * @param output
536         *            the <code>OutputStream</code> to write to
537         * @throws NullPointerException
538         *             if output is null
539         * @throws IOException
540         *             if an I/O error occurs
541         * @since 1.1
542         */
543        public static void write(final String data, final OutputStream output) throws IOException
544        {
545                if (data != null)
546                {
547                        output.write(data.getBytes());
548                }
549        }
550
551        /**
552         * Writes chars from a <code>String</code> to bytes on an <code>OutputStream</code> using the
553         * specified character encoding.
554         * <p>
555         * Character encoding names can be found at <a
556         * href="http://www.iana.org/assignments/character-sets">IANA</a>.
557         * <p>
558         * This method uses {@link String#getBytes(String)}.
559         * 
560         * @param data
561         *            the <code>String</code> to write, null ignored
562         * @param output
563         *            the <code>OutputStream</code> to write to
564         * @param encoding
565         *            the encoding to use, null means platform default
566         * @throws NullPointerException
567         *             if output is null
568         * @throws IOException
569         *             if an I/O error occurs
570         * @since 1.1
571         */
572        public static void write(final String data, final OutputStream output, final String encoding)
573                throws IOException
574        {
575                if (data != null)
576                {
577                        if (encoding == null)
578                        {
579                                write(data, output);
580                        }
581                        else
582                        {
583                                output.write(data.getBytes(encoding));
584                        }
585                }
586        }
587
588        /**
589         * Writes chars from a <code>AppendingStringBuffer</code> to a <code>Writer</code>.
590         * 
591         * @param data
592         *            the <code>AppendingStringBuffer</code> to write, null ignored
593         * @param output
594         *            the <code>Writer</code> to write to
595         * @throws NullPointerException
596         *             if output is null
597         * @throws IOException
598         *             if an I/O error occurs
599         * @since 1.1
600         */
601        public static void write(final StringBuilder data, final Writer output) throws IOException
602        {
603                if (data != null)
604                {
605                        output.write(data.toString());
606                }
607        }
608
609        /**
610         * Writes chars from a <code>AppendingStringBuffer</code> to bytes on an
611         * <code>OutputStream</code> using the default character encoding of the platform.
612         * <p>
613         * This method uses {@link String#getBytes()}.
614         * 
615         * @param data
616         *            the <code>AppendingStringBuffer</code> to write, null ignored
617         * @param output
618         *            the <code>OutputStream</code> to write to
619         * @throws NullPointerException
620         *             if output is null
621         * @throws IOException
622         *             if an I/O error occurs
623         * @since 1.1
624         */
625        public static void write(final StringBuilder data, final OutputStream output)
626                throws IOException
627        {
628                if (data != null)
629                {
630                        output.write(data.toString().getBytes());
631                }
632        }
633
634        /**
635         * Writes chars from a <code>AppendingStringBuffer</code> to bytes on an
636         * <code>OutputStream</code> using the specified character encoding.
637         * <p>
638         * Character encoding names can be found at <a
639         * href="http://www.iana.org/assignments/character-sets">IANA</a>.
640         * <p>
641         * This method uses {@link String#getBytes(String)}.
642         * 
643         * @param data
644         *            the <code>AppendingStringBuffer</code> to write, null ignored
645         * @param output
646         *            the <code>OutputStream</code> to write to
647         * @param encoding
648         *            the encoding to use, null means platform default
649         * @throws NullPointerException
650         *             if output is null
651         * @throws IOException
652         *             if an I/O error occurs
653         * @since 1.1
654         */
655        public static void write(final StringBuilder data, final OutputStream output,
656                final String encoding) throws IOException
657        {
658                if (data != null)
659                {
660                        if (encoding == null)
661                        {
662                                write(data, output);
663                        }
664                        else
665                        {
666                                output.write(data.toString().getBytes(encoding));
667                        }
668                }
669        }
670
671        /**
672         * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
673         * <p>
674         * This method buffers the input internally, so there is no need to use a
675         * <code>BufferedInputStream</code>.
676         * 
677         * @param input
678         *            the <code>InputStream</code> to read from
679         * @param output
680         *            the <code>OutputStream</code> to write to
681         * @return the number of bytes copied
682         * @throws NullPointerException
683         *             if the input or output is null
684         * @throws IOException
685         *             if an I/O error occurs
686         * @since 1.1
687         */
688        public static int copy(final InputStream input, final OutputStream output) throws IOException
689        {
690                byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
691                int count = 0;
692                int n = 0;
693                while (-1 != (n = input.read(buffer)))
694                {
695                        output.write(buffer, 0, n);
696                        count += n;
697                }
698                return count;
699        }
700
701        /**
702         * Copy bytes from an <code>InputStream</code> to chars on a <code>Writer</code> using the
703         * default character encoding of the platform.
704         * <p>
705         * This method buffers the input internally, so there is no need to use a
706         * <code>BufferedInputStream</code>.
707         * <p>
708         * This method uses {@link InputStreamReader}.
709         * 
710         * @param input
711         *            the <code>InputStream</code> to read from
712         * @param output
713         *            the <code>Writer</code> to write to
714         * @throws NullPointerException
715         *             if the input or output is null
716         * @throws IOException
717         *             if an I/O error occurs
718         * @since 1.1
719         */
720        public static void copy(final InputStream input, final Writer output) throws IOException
721        {
722                InputStreamReader in = new InputStreamReader(input);
723                copy(in, output);
724        }
725
726        /**
727         * Copy bytes from an <code>InputStream</code> to chars on a <code>Writer</code> using the
728         * specified character encoding.
729         * <p>
730         * This method buffers the input internally, so there is no need to use a
731         * <code>BufferedInputStream</code>.
732         * <p>
733         * Character encoding names can be found at <a
734         * href="http://www.iana.org/assignments/character-sets">IANA</a>.
735         * <p>
736         * This method uses {@link InputStreamReader}.
737         * 
738         * @param input
739         *            the <code>InputStream</code> to read from
740         * @param output
741         *            the <code>Writer</code> to write to
742         * @param encoding
743         *            the encoding to use, null means platform default
744         * @throws NullPointerException
745         *             if the input or output is null
746         * @throws IOException
747         *             if an I/O error occurs
748         * @since 1.1
749         */
750        public static void copy(final InputStream input, final Writer output, final String encoding)
751                throws IOException
752        {
753                if (encoding == null)
754                {
755                        copy(input, output);
756                }
757                else
758                {
759                        InputStreamReader in = new InputStreamReader(input, encoding);
760                        copy(in, output);
761                }
762        }
763
764        /**
765         * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
766         * <p>
767         * This method buffers the input internally, so there is no need to use a
768         * <code>BufferedReader</code>.
769         * 
770         * @param input
771         *            the <code>Reader</code> to read from
772         * @param output
773         *            the <code>Writer</code> to write to
774         * @return the number of characters copied
775         * @throws NullPointerException
776         *             if the input or output is null
777         * @throws IOException
778         *             if an I/O error occurs
779         * @since 1.1
780         */
781        public static int copy(final Reader input, final Writer output) throws IOException
782        {
783                char[] buffer = new char[DEFAULT_BUFFER_SIZE];
784                int count = 0;
785                int n = 0;
786                while (-1 != (n = input.read(buffer)))
787                {
788                        output.write(buffer, 0, n);
789                        count += n;
790                }
791                return count;
792        }
793
794        /**
795         * Copy chars from a <code>Reader</code> to bytes on an <code>OutputStream</code> using the
796         * default character encoding of the platform, and calling flush.
797         * <p>
798         * This method buffers the input internally, so there is no need to use a
799         * <code>BufferedReader</code>.
800         * <p>
801         * Due to the implementation of OutputStreamWriter, this method performs a flush.
802         * <p>
803         * This method uses {@link OutputStreamWriter}.
804         * 
805         * @param input
806         *            the <code>Reader</code> to read from
807         * @param output
808         *            the <code>OutputStream</code> to write to
809         * @throws NullPointerException
810         *             if the input or output is null
811         * @throws IOException
812         *             if an I/O error occurs
813         * @since 1.1
814         */
815        public static void copy(final Reader input, final OutputStream output) throws IOException
816        {
817                OutputStreamWriter out = new OutputStreamWriter(output);
818                copy(input, out);
819                out.flush();
820        }
821
822        /**
823         * Copy chars from a <code>Reader</code> to bytes on an <code>OutputStream</code> using the
824         * specified character encoding, and calling flush.
825         * <p>
826         * This method buffers the input internally, so there is no need to use a
827         * <code>BufferedReader</code>.
828         * <p>
829         * Character encoding names can be found at <a
830         * href="http://www.iana.org/assignments/character-sets">IANA</a>.
831         * <p>
832         * Due to the implementation of OutputStreamWriter, this method performs a flush.
833         * <p>
834         * This method uses {@link OutputStreamWriter}.
835         * 
836         * @param input
837         *            the <code>Reader</code> to read from
838         * @param output
839         *            the <code>OutputStream</code> to write to
840         * @param encoding
841         *            the encoding to use, null means platform default
842         * @throws NullPointerException
843         *             if the input or output is null
844         * @throws IOException
845         *             if an I/O error occurs
846         * @since 1.1
847         */
848        public static void copy(final Reader input, final OutputStream output, final String encoding)
849                throws IOException
850        {
851                if (encoding == null)
852                {
853                        copy(input, output);
854                }
855                else
856                {
857                        OutputStreamWriter out = new OutputStreamWriter(output, encoding);
858                        copy(input, out);
859                        out.flush();
860                }
861        }
862
863        /**
864         * Compare the contents of two Streams to determine if they are equal or not.
865         * <p>
866         * This method buffers the input internally using <code>BufferedInputStream</code> if they are
867         * not already buffered.
868         * 
869         * @param input1
870         *            the first stream
871         * @param input2
872         *            the second stream
873         * @return true if the content of the streams are equal or they both don't exist, false
874         *         otherwise
875         * @throws NullPointerException
876         *             if either input is null
877         * @throws IOException
878         *             if an I/O error occurs
879         */
880        public static boolean contentEquals(InputStream input1, InputStream input2) throws IOException
881        {
882                if (!(input1 instanceof BufferedInputStream))
883                {
884                        input1 = new BufferedInputStream(input1);
885                }
886                if (!(input2 instanceof BufferedInputStream))
887                {
888                        input2 = new BufferedInputStream(input2);
889                }
890
891                int ch = input1.read();
892                while (-1 != ch)
893                {
894                        int ch2 = input2.read();
895                        if (ch != ch2)
896                        {
897                                return false;
898                        }
899                        ch = input1.read();
900                }
901
902                int ch2 = input2.read();
903                return (ch2 == -1);
904        }
905
906        /**
907         * Compare the contents of two Readers to determine if they are equal or not.
908         * <p>
909         * This method buffers the input internally using <code>BufferedReader</code> if they are not
910         * already buffered.
911         * 
912         * @param input1
913         *            the first reader
914         * @param input2
915         *            the second reader
916         * @return true if the content of the readers are equal or they both don't exist, false
917         *         otherwise
918         * @throws NullPointerException
919         *             if either input is null
920         * @throws IOException
921         *             if an I/O error occurs
922         * @since 1.1
923         */
924        public static boolean contentEquals(Reader input1, Reader input2) throws IOException
925        {
926                if (!(input1 instanceof BufferedReader))
927                {
928                        input1 = new BufferedReader(input1);
929                }
930                if (!(input2 instanceof BufferedReader))
931                {
932                        input2 = new BufferedReader(input2);
933                }
934
935                int ch = input1.read();
936                while (-1 != ch)
937                {
938                        int ch2 = input2.read();
939                        if (ch != ch2)
940                        {
941                                return false;
942                        }
943                        ch = input1.read();
944                }
945
946                int ch2 = input2.read();
947                return (ch2 == -1);
948        }
949}