/* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.util; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.CharArrayWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import org.bouncycastle.util.io.Streams; import org.cryptacular.StreamException; import org.cryptacular.io.ChunkHandler; /** * Utility methods for stream handling. * * @author Middleware Services */ public final class StreamUtil { /** * Buffer size of chunked operations, e.g. {@link #pipeAll(java.io.InputStream, java.io.OutputStream, * org.cryptacular.io.ChunkHandler)}. */ public static final int CHUNK_SIZE = 1024; /** Private method of utility class. */ private StreamUtil() {} /** * Reads all the data from the file at the given path. * * @param path Path to file. * * @return Byte array of data read from file. * * @throws StreamException on stream IO errors. */ public static byte[] readAll(final String path) throws StreamException { return readAll(new File(path)); } /** * Reads all the data from the given file. * * @param file File to read. * * @return Byte array of data read from file. * * @throws StreamException on stream IO errors. */ public static byte[] readAll(final File file) throws StreamException { final InputStream input = makeStream(file); try { return readAll(input, (int) file.length()); } finally { closeStream(input); } } /** * Reads all the data from the given input stream. * * @param input Input stream to read. * * @return Byte array of data read from stream. * * @throws StreamException on stream IO errors. */ public static byte[] readAll(final InputStream input) throws StreamException { return readAll(input, 1024); } /** * Reads all the data from the given input stream. * * @param input Input stream to read. * @param sizeHint Estimate of amount of data to be read in bytes. * * @return Byte array of data read from stream. * * @throws StreamException on stream IO errors. */ public static byte[] readAll(final InputStream input, final int sizeHint) throws StreamException { final ByteArrayOutputStream output = new ByteArrayOutputStream(sizeHint); try { Streams.pipeAll(input, output); } catch (IOException e) { throw new StreamException(e); } finally { closeStream(input); closeStream(output); } return output.toByteArray(); } /** * Reads all data from the given reader. * * @param reader Reader over character data. * * @return Data read from reader. * * @throws StreamException on stream IO errors. */ public static String readAll(final Reader reader) throws StreamException { return readAll(reader, 1024); } /** * Reads all data from the given reader. * * @param reader Reader over character data. * @param sizeHint Estimate of amount of data to be read in number of characters. * * @return Data read from reader. * * @throws StreamException on stream IO errors. */ public static String readAll(final Reader reader, final int sizeHint) throws StreamException { final CharArrayWriter writer = new CharArrayWriter(sizeHint); final char[] buffer = new char[CHUNK_SIZE]; int len; try { while ((len = reader.read(buffer)) > 0) { writer.write(buffer, 0, len); } } catch (IOException e) { throw new StreamException(e); } finally { closeReader(reader); closeWriter(writer); } return writer.toString(); } /** * Pipes an input stream into an output stream with chunked processing. * * @param in Input stream providing data to process. * @param out Output stream holding processed data. * @param handler Arbitrary handler for processing input stream. * * @throws StreamException on stream IO errors. */ public static void pipeAll(final InputStream in, final OutputStream out, final ChunkHandler handler) throws StreamException { final byte[] buffer = new byte[CHUNK_SIZE]; int count; try { while ((count = in.read(buffer)) > 0) { handler.handle(buffer, 0, count, out); } } catch (IOException e) { throw new StreamException(e); } } /** * Creates an input stream around the given file. * * @param file Input stream source. * * @return Input stream around file. * * @throws StreamException on stream IO errors. */ public static InputStream makeStream(final File file) throws StreamException { try { return new BufferedInputStream(new FileInputStream(file)); } catch (FileNotFoundException e) { throw new StreamException(file + " does not exist"); } } /** * Creates a reader around the given file that presumably contains character data. * * @param file Reader source. * * @return Reader around file. * * @throws StreamException on stream IO errors. */ public static Reader makeReader(final File file) throws StreamException { try { return new InputStreamReader(new BufferedInputStream(new FileInputStream(file))); } catch (FileNotFoundException e) { throw new StreamException(file + " does not exist"); } } /** * Closes the given stream and swallows exceptions that may arise during the process. * * @param in Input stream to close. */ public static void closeStream(final InputStream in) { try { in.close(); } catch (IOException e) { System.err.println("Error closing " + in + ": " + e); } } /** * Closes the given stream and swallows exceptions that may arise during the process. * * @param out Output stream to close. */ public static void closeStream(final OutputStream out) { try { out.close(); } catch (IOException e) { System.err.println("Error closing " + out + ": " + e); } } /** * Closes the given reader and swallows exceptions that may arise during the process. * * @param reader Reader to close. */ public static void closeReader(final Reader reader) { try { reader.close(); } catch (IOException e) { System.err.println("Error closing " + reader + ": " + e); } } /** * Closes the given writer and swallows exceptions that may arise during the process. * * @param writer Writer to close. */ public static void closeWriter(final Writer writer) { try { writer.close(); } catch (IOException e) { System.err.println("Error closing " + writer + ": " + e); } } }