001: /****************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one *
003: * or more contributor license agreements. See the NOTICE file *
004: * distributed with this work for additional information *
005: * regarding copyright ownership. The ASF licenses this file *
006: * to you under the Apache License, Version 2.0 (the *
007: * "License"); you may not use this file except in compliance *
008: * with the License. You may obtain a copy of the License at *
009: * *
010: * http://www.apache.org/licenses/LICENSE-2.0 *
011: * *
012: * Unless required by applicable law or agreed to in writing, *
013: * software distributed under the License is distributed on an *
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015: * KIND, either express or implied. See the License for the *
016: * specific language governing permissions and limitations *
017: * under the License. *
018: ****************************************************************/package org.apache.james.util.io;
019:
020: import java.io.BufferedInputStream;
021: import java.io.ByteArrayInputStream;
022: import java.io.ByteArrayOutputStream;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.InputStreamReader;
026: import java.io.OutputStream;
027: import java.io.OutputStreamWriter;
028: import java.io.Reader;
029: import java.io.StringReader;
030: import java.io.StringWriter;
031: import java.io.Writer;
032:
033: /**
034: * General IO Stream manipulation.
035: * <p>
036: * This class provides static utility methods for input/output operations, particularly buffered
037: * copying between sources (<code>InputStream</code>, <code>Reader</code>, <code>String</code> and
038: * <code>byte[]</code>) and destinations (<code>OutputStream</code>, <code>Writer</code>,
039: * <code>String</code> and <code>byte[]</code>).
040: * </p>
041: *
042: * <p>Unless otherwise noted, these <code>copy</code> methods do <em>not</em> flush or close the
043: * streams. Often, doing so would require making non-portable assumptions about the streams' origin
044: * and further use. This means that both streams' <code>close()</code> methods must be called after
045: * copying. if one omits this step, then the stream resources (sockets, file descriptors) are
046: * released when the associated Stream is garbage-collected. It is not a good idea to rely on this
047: * mechanism. For a good overview of the distinction between "memory management" and "resource
048: * management", see <a href="http://www.unixreview.com/articles/1998/9804/9804ja/ja.htm">this
049: * UnixReview article</a></p>
050: *
051: * <p>For each <code>copy</code> method, a variant is provided that allows the caller to specify the
052: * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this
053: * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data
054: * transfers.</p>
055: *
056: * <p>For byte-to-char methods, a <code>copy</code> variant allows the encoding to be selected
057: * (otherwise the platform default is used).</p>
058: *
059: * <p>The <code>copy</code> methods use an internal buffer when copying. It is therefore advisable
060: * <em>not</em> to deliberately wrap the stream arguments to the <code>copy</code> methods in
061: * <code>Buffered*</code> streams. For example, don't do the
062: * following:</p>
063: *
064: * <code>copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );</code>
065: *
066: * <p>The rationale is as follows:</p>
067: *
068: * <p>Imagine that an InputStream's read() is a very expensive operation, which would usually suggest
069: * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent
070: * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to
071: * fill an internal buffer, from which further <code>read</code> requests can inexpensively get
072: * their data (until the buffer runs out).</p>
073: * <p>However, the <code>copy</code> methods do the same thing, keeping an internal buffer,
074: * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers
075: * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer
076: * management hurts performance slightly (about 3%, according to some simple experiments).</p>
077: *
078: * @author Peter Donald
079: * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
080: * @version CVS $Revision: 494012 $ $Date: 2007-01-08 11:23:58 +0100 (Mo, 08 Jan 2007) $
081: * @since 4.0
082: */
083:
084: /*
085: * Behold, intrepid explorers; a map of this class:
086: *
087: * Method Input Output Dependency
088: * ------ ----- ------ -------
089: * 1 copy InputStream OutputStream (primitive)
090: * 2 copy Reader Writer (primitive)
091: *
092: * 3 copy InputStream Writer 2
093: * 4 toString InputStream String 3
094: * 5 toByteArray InputStream byte[] 1
095: *
096: * 6 copy Reader OutputStream 2
097: * 7 toString Reader String 2
098: * 8 toByteArray Reader byte[] 6
099: *
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: private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
117:
118: /**
119: * Private constructor to prevent instantiation.
120: */
121: private IOUtil() {
122: }
123:
124: /**
125: * Unconditionally close an <code>Reader</code>.
126: * Equivalent to {@link Reader#close()}, except any exceptions will be ignored.
127: *
128: * @param input A (possibly null) Reader
129: */
130: public static void shutdownReader(final Reader input) {
131: if (null == input) {
132: return;
133: }
134:
135: try {
136: input.close();
137: } catch (final IOException ioe) {
138: }
139: }
140:
141: /**
142: * Unconditionally close an <code>Writer</code>.
143: * Equivalent to {@link Writer#close()}, except any exceptions will be ignored.
144: *
145: * @param output A (possibly null) Writer
146: */
147: public static void shutdownWriter(final Writer output) {
148: if (null == output) {
149: return;
150: }
151:
152: try {
153: output.close();
154: } catch (final IOException ioe) {
155: }
156: }
157:
158: /**
159: * Unconditionally close an <code>OutputStream</code>.
160: * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored.
161: * @param output A (possibly null) OutputStream
162: */
163: public static void shutdownStream(final OutputStream output) {
164: if (null == output) {
165: return;
166: }
167:
168: try {
169: output.close();
170: } catch (final IOException ioe) {
171: }
172: }
173:
174: /**
175: * Unconditionally close an <code>InputStream</code>.
176: * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored.
177: * @param input A (possibly null) InputStream
178: */
179: public static void shutdownStream(final InputStream input) {
180: if (null == input) {
181: return;
182: }
183:
184: try {
185: input.close();
186: } catch (final IOException ioe) {
187: }
188: }
189:
190: ///////////////////////////////////////////////////////////////
191: // Core copy methods
192: ///////////////////////////////////////////////////////////////
193:
194: /**
195: * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
196: */
197: public static void copy(final InputStream input,
198: final OutputStream output) throws IOException {
199: copy(input, output, DEFAULT_BUFFER_SIZE);
200: }
201:
202: /**
203: * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
204: * @param bufferSize Size of internal buffer to use.
205: */
206: public static void copy(final InputStream input,
207: final OutputStream output, final int bufferSize)
208: throws IOException {
209: final byte[] buffer = new byte[bufferSize];
210: int n = 0;
211: while (-1 != (n = input.read(buffer))) {
212: output.write(buffer, 0, n);
213: }
214: }
215:
216: /**
217: * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
218: */
219: public static void copy(final Reader input, final Writer output)
220: throws IOException {
221: copy(input, output, DEFAULT_BUFFER_SIZE);
222: }
223:
224: /**
225: * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
226: * @param bufferSize Size of internal buffer to use.
227: */
228: public static void copy(final Reader input, final Writer output,
229: final int bufferSize) throws IOException {
230: final char[] buffer = new char[bufferSize];
231: int n = 0;
232: while (-1 != (n = input.read(buffer))) {
233: output.write(buffer, 0, n);
234: }
235: }
236:
237: ///////////////////////////////////////////////////////////////
238: // Derived copy methods
239: // InputStream -> *
240: ///////////////////////////////////////////////////////////////
241:
242: ///////////////////////////////////////////////////////////////
243: // InputStream -> Writer
244:
245: /**
246: * Copy and convert bytes from an <code>InputStream</code> to chars on a
247: * <code>Writer</code>.
248: * The platform's default encoding is used for the byte-to-char conversion.
249: */
250: public static void copy(final InputStream input, final Writer output)
251: throws IOException {
252: copy(input, output, DEFAULT_BUFFER_SIZE);
253: }
254:
255: /**
256: * Copy and convert bytes from an <code>InputStream</code> to chars on a
257: * <code>Writer</code>.
258: * The platform's default encoding is used for the byte-to-char conversion.
259: * @param bufferSize Size of internal buffer to use.
260: */
261: public static void copy(final InputStream input,
262: final Writer output, final int bufferSize)
263: throws IOException {
264: final InputStreamReader in = new InputStreamReader(input);
265: copy(in, output, bufferSize);
266: }
267:
268: /**
269: * Copy and convert bytes from an <code>InputStream</code> to chars on a
270: * <code>Writer</code>, using the specified encoding.
271: * @param encoding The name of a supported character encoding. See the
272: * <a href="http://www.iana.org/assignments/character-sets">IANA
273: * Charset Registry</a> for a list of valid encoding types.
274: */
275: public static void copy(final InputStream input,
276: final Writer output, final String encoding)
277: throws IOException {
278: final InputStreamReader in = new InputStreamReader(input,
279: encoding);
280: copy(in, output);
281: }
282:
283: /**
284: * Copy and convert bytes from an <code>InputStream</code> to chars on a
285: * <code>Writer</code>, using the specified encoding.
286: * @param encoding The name of a supported character encoding. See the
287: * <a href="http://www.iana.org/assignments/character-sets">IANA
288: * Charset Registry</a> for a list of valid encoding types.
289: * @param bufferSize Size of internal buffer to use.
290: */
291: public static void copy(final InputStream input,
292: final Writer output, final String encoding,
293: final int bufferSize) throws IOException {
294: final InputStreamReader in = new InputStreamReader(input,
295: encoding);
296: copy(in, output, bufferSize);
297: }
298:
299: ///////////////////////////////////////////////////////////////
300: // InputStream -> String
301:
302: /**
303: * Get the contents of an <code>InputStream</code> as a String.
304: * The platform's default encoding is used for the byte-to-char conversion.
305: */
306: public static String toString(final InputStream input)
307: throws IOException {
308: return toString(input, DEFAULT_BUFFER_SIZE);
309: }
310:
311: /**
312: * Get the contents of an <code>InputStream</code> as a String.
313: * The platform's default encoding is used for the byte-to-char conversion.
314: * @param bufferSize Size of internal buffer to use.
315: */
316: public static String toString(final InputStream input,
317: final int bufferSize) throws IOException {
318: final StringWriter sw = new StringWriter();
319: copy(input, sw, bufferSize);
320: return sw.toString();
321: }
322:
323: /**
324: * Get the contents of an <code>InputStream</code> as a String.
325: * @param encoding The name of a supported character encoding. See the
326: * <a href="http://www.iana.org/assignments/character-sets">IANA
327: * Charset Registry</a> for a list of valid encoding types.
328: */
329: public static String toString(final InputStream input,
330: final String encoding) throws IOException {
331: return toString(input, encoding, DEFAULT_BUFFER_SIZE);
332: }
333:
334: /**
335: * Get the contents of an <code>InputStream</code> as a String.
336: * @param encoding The name of a supported character encoding. See the
337: * <a href="http://www.iana.org/assignments/character-sets">IANA
338: * Charset Registry</a> for a list of valid encoding types.
339: * @param bufferSize Size of internal buffer to use.
340: */
341: public static String toString(final InputStream input,
342: final String encoding, final int bufferSize)
343: throws IOException {
344: final StringWriter sw = new StringWriter();
345: copy(input, sw, encoding, bufferSize);
346: return sw.toString();
347: }
348:
349: ///////////////////////////////////////////////////////////////
350: // InputStream -> byte[]
351:
352: /**
353: * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
354: */
355: public static byte[] toByteArray(final InputStream input)
356: throws IOException {
357: return toByteArray(input, DEFAULT_BUFFER_SIZE);
358: }
359:
360: /**
361: * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
362: * @param bufferSize Size of internal buffer to use.
363: */
364: public static byte[] toByteArray(final InputStream input,
365: final int bufferSize) throws IOException {
366: final ByteArrayOutputStream output = new ByteArrayOutputStream();
367: copy(input, output, bufferSize);
368: return output.toByteArray();
369: }
370:
371: ///////////////////////////////////////////////////////////////
372: // Derived copy methods
373: // Reader -> *
374: ///////////////////////////////////////////////////////////////
375:
376: ///////////////////////////////////////////////////////////////
377: // Reader -> OutputStream
378: /**
379: * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
380: * flush the <code>OutputStream</code>.
381: */
382: public static void copy(final Reader input,
383: final OutputStream output) throws IOException {
384: copy(input, output, DEFAULT_BUFFER_SIZE);
385: }
386:
387: /**
388: * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
389: * flush the <code>OutputStream</code>.
390: * @param bufferSize Size of internal buffer to use.
391: */
392: public static void copy(final Reader input,
393: final OutputStream output, final int bufferSize)
394: throws IOException {
395: final OutputStreamWriter out = new OutputStreamWriter(output);
396: copy(input, out, bufferSize);
397: // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
398: // here.
399: out.flush();
400: }
401:
402: ///////////////////////////////////////////////////////////////
403: // Reader -> String
404: /**
405: * Get the contents of a <code>Reader</code> as a String.
406: */
407: public static String toString(final Reader input)
408: throws IOException {
409: return toString(input, DEFAULT_BUFFER_SIZE);
410: }
411:
412: /**
413: * Get the contents of a <code>Reader</code> as a String.
414: * @param bufferSize Size of internal buffer to use.
415: */
416: public static String toString(final Reader input,
417: final int bufferSize) throws IOException {
418: final StringWriter sw = new StringWriter();
419: copy(input, sw, bufferSize);
420: return sw.toString();
421: }
422:
423: ///////////////////////////////////////////////////////////////
424: // Reader -> byte[]
425: /**
426: * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
427: */
428: public static byte[] toByteArray(final Reader input)
429: throws IOException {
430: return toByteArray(input, DEFAULT_BUFFER_SIZE);
431: }
432:
433: /**
434: * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
435: * @param bufferSize Size of internal buffer to use.
436: */
437: public static byte[] toByteArray(final Reader input,
438: final int bufferSize) throws IOException {
439: ByteArrayOutputStream output = new ByteArrayOutputStream();
440: copy(input, output, bufferSize);
441: return output.toByteArray();
442: }
443:
444: ///////////////////////////////////////////////////////////////
445: // Derived copy methods
446: // String -> *
447: ///////////////////////////////////////////////////////////////
448:
449: ///////////////////////////////////////////////////////////////
450: // String -> OutputStream
451:
452: /**
453: * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
454: * flush the <code>OutputStream</code>.
455: */
456: public static void copy(final String input,
457: final OutputStream output) throws IOException {
458: copy(input, output, DEFAULT_BUFFER_SIZE);
459: }
460:
461: /**
462: * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
463: * flush the <code>OutputStream</code>.
464: * @param bufferSize Size of internal buffer to use.
465: */
466: public static void copy(final String input,
467: final OutputStream output, final int bufferSize)
468: throws IOException {
469: final StringReader in = new StringReader(input);
470: final OutputStreamWriter out = new OutputStreamWriter(output);
471: copy(in, out, bufferSize);
472: // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
473: // here.
474: out.flush();
475: }
476:
477: ///////////////////////////////////////////////////////////////
478: // String -> Writer
479:
480: /**
481: * Copy chars from a <code>String</code> to a <code>Writer</code>.
482: */
483: public static void copy(final String input, final Writer output)
484: throws IOException {
485: output.write(input);
486: }
487:
488: ///////////////////////////////////////////////////////////////
489: // String -> byte[]
490: /**
491: * Get the contents of a <code>String</code> as a <code>byte[]</code>.
492: */
493: public static byte[] toByteArray(final String input)
494: throws IOException {
495: return toByteArray(input, DEFAULT_BUFFER_SIZE);
496: }
497:
498: /**
499: * Get the contents of a <code>String</code> as a <code>byte[]</code>.
500: * @param bufferSize Size of internal buffer to use.
501: */
502: public static byte[] toByteArray(final String input,
503: final int bufferSize) throws IOException {
504: ByteArrayOutputStream output = new ByteArrayOutputStream();
505: copy(input, output, bufferSize);
506: return output.toByteArray();
507: }
508:
509: ///////////////////////////////////////////////////////////////
510: // Derived copy methods
511: // byte[] -> *
512: ///////////////////////////////////////////////////////////////
513:
514: ///////////////////////////////////////////////////////////////
515: // byte[] -> Writer
516:
517: /**
518: * Copy and convert bytes from a <code>byte[]</code> to chars on a
519: * <code>Writer</code>.
520: * The platform's default encoding is used for the byte-to-char conversion.
521: */
522: public static void copy(final byte[] input, final Writer output)
523: throws IOException {
524: copy(input, output, DEFAULT_BUFFER_SIZE);
525: }
526:
527: /**
528: * Copy and convert bytes from a <code>byte[]</code> to chars on a
529: * <code>Writer</code>.
530: * The platform's default encoding is used for the byte-to-char conversion.
531: * @param bufferSize Size of internal buffer to use.
532: */
533: public static void copy(final byte[] input, final Writer output,
534: final int bufferSize) throws IOException {
535: final ByteArrayInputStream in = new ByteArrayInputStream(input);
536: copy(in, output, bufferSize);
537: }
538:
539: /**
540: * Copy and convert bytes from a <code>byte[]</code> to chars on a
541: * <code>Writer</code>, using the specified encoding.
542: * @param encoding The name of a supported character encoding. See the
543: * <a href="http://www.iana.org/assignments/character-sets">IANA
544: * Charset Registry</a> for a list of valid encoding types.
545: */
546: public static void copy(final byte[] input, final Writer output,
547: final String encoding) throws IOException {
548: final ByteArrayInputStream in = new ByteArrayInputStream(input);
549: copy(in, output, encoding);
550: }
551:
552: /**
553: * Copy and convert bytes from a <code>byte[]</code> to chars on a
554: * <code>Writer</code>, using the specified encoding.
555: * @param encoding The name of a supported character encoding. See the
556: * <a href="http://www.iana.org/assignments/character-sets">IANA
557: * Charset Registry</a> for a list of valid encoding types.
558: * @param bufferSize Size of internal buffer to use.
559: */
560: public static void copy(final byte[] input, final Writer output,
561: final String encoding, final int bufferSize)
562: throws IOException {
563: final ByteArrayInputStream in = new ByteArrayInputStream(input);
564: copy(in, output, encoding, bufferSize);
565: }
566:
567: ///////////////////////////////////////////////////////////////
568: // byte[] -> String
569:
570: /**
571: * Get the contents of a <code>byte[]</code> as a String.
572: * The platform's default encoding is used for the byte-to-char conversion.
573: */
574: public static String toString(final byte[] input)
575: throws IOException {
576: return toString(input, DEFAULT_BUFFER_SIZE);
577: }
578:
579: /**
580: * Get the contents of a <code>byte[]</code> as a String.
581: * The platform's default encoding is used for the byte-to-char conversion.
582: * @param bufferSize Size of internal buffer to use.
583: */
584: public static String toString(final byte[] input,
585: final int bufferSize) throws IOException {
586: final StringWriter sw = new StringWriter();
587: copy(input, sw, bufferSize);
588: return sw.toString();
589: }
590:
591: /**
592: * Get the contents of a <code>byte[]</code> as a String.
593: * @param encoding The name of a supported character encoding. See the
594: * <a href="http://www.iana.org/assignments/character-sets">IANA
595: * Charset Registry</a> for a list of valid encoding types.
596: */
597: public static String toString(final byte[] input,
598: final String encoding) throws IOException {
599: return toString(input, encoding, DEFAULT_BUFFER_SIZE);
600: }
601:
602: /**
603: * Get the contents of a <code>byte[]</code> as a String.
604: * @param encoding The name of a supported character encoding. See the
605: * <a href="http://www.iana.org/assignments/character-sets">IANA
606: * Charset Registry</a> for a list of valid encoding types.
607: * @param bufferSize Size of internal buffer to use.
608: */
609: public static String toString(final byte[] input,
610: final String encoding, final int bufferSize)
611: throws IOException {
612: final StringWriter sw = new StringWriter();
613: copy(input, sw, encoding, bufferSize);
614: return sw.toString();
615: }
616:
617: ///////////////////////////////////////////////////////////////
618: // byte[] -> OutputStream
619:
620: /**
621: * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
622: */
623: public static void copy(final byte[] input,
624: final OutputStream output) throws IOException {
625: copy(input, output, DEFAULT_BUFFER_SIZE);
626: }
627:
628: /**
629: * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
630: * @param bufferSize Size of internal buffer to use.
631: */
632: public static void copy(final byte[] input,
633: final OutputStream output, final int bufferSize)
634: throws IOException {
635: output.write(input);
636: }
637:
638: /**
639: * Compare the contents of two Streams to determine if they are equal or not.
640: *
641: * @param input1 the first stream
642: * @param input2 the second stream
643: * @return true if the content of the streams are equal or they both don't exist, false otherwise
644: */
645: public static boolean contentEquals(final InputStream input1,
646: final InputStream input2) throws IOException {
647: final InputStream bufferedInput1 = new BufferedInputStream(
648: input1);
649: final InputStream bufferedInput2 = new BufferedInputStream(
650: input2);
651:
652: int ch = bufferedInput1.read();
653: while (-1 != ch) {
654: final int ch2 = bufferedInput2.read();
655: if (ch != ch2) {
656: return false;
657: }
658: ch = bufferedInput1.read();
659: }
660:
661: final int ch2 = bufferedInput2.read();
662: if (-1 != ch2) {
663: return false;
664: } else {
665: return true;
666: }
667: }
668: }
|