package org.yajul.util;
import java.io.*;
/**
* Provides stream and reader/writer copying functions.
* <br>
* User: jdavis
* Date: Jan 28, 2004
* Time: 5:53:42 PM
*
* @author jdavis
*/
public class Copier {
private static int EOS = -1;
/**
* The default buffer size. *
*/
public static int DEFAULT_BUFFER_SIZE = 1024;
/**
* Use this to indicate a non-length limited copy. *
*/
public static int UNLIMITED = -1;
/**
* No callback constant.
*/
public static final Callback NO_CALLBACK = null;
public interface Callback {
/**
* The copying has started.
*/
void startOfStream();
/**
* Progress callback for InputStream/OutputStream copy.
* @param buf the current buffer about to be written.
* @param length the number of bytes in the buffer
* @param total the total number of bytes so far
* @return true if it's okay to keep going, false to stop the copy loop
*/
boolean beforeWrite(byte[] buf, int length, int total);
/**
* Progress callback for Reader/Writer copy.
* @param buf the current buffer about to be written.
* @param length the number of bytes in the buffer
* @param total the total number of bytes so far
* @return true if it's okay to keep going, false to stop the copy loop
*/
boolean beforeWrite(char[] buf, int length, int total);
/**
* The end of the input stream was reached.
* @param total the total number of bytes copied
*/
void endOfStream(int total);
}
/**
* Copies the input stream into the output stream in an efficient manner.
* This version does not synchronize on the streams, so it is not safe
* to use when the streams are being accessed by multiple threads.
*
* @param in The input stream.
* @param out The output stream. If this is null, the input will be
* discarded, similar to piping to /dev/null on UN*X.
* @param bufsz The size of the buffer to use.
* @param limit The number of bytes to copy, or UNLIMITED (-1) to copy
* until the end of the input stream.
* @return int The number of bytes copied.
* @throws java.io.IOException When the stream could not be copied.
*/
public static int copy(InputStream in, OutputStream out, int bufsz, int limit) throws IOException {
return copy(in,out,bufsz,limit,NO_CALLBACK);
}
/**
* Copies the input stream into the output stream in an efficient manner.
* This version does not synchronize on the streams, so it is not safe
* to use when the streams are being accessed by multiple threads.
*
* @param in The input stream.
* @param out The output stream. If this is null, the input will be
* discarded, similar to piping to /dev/null on UN*X.
* @param bufsz The size of the buffer to use.
* @param limit The number of bytes to copy, or UNLIMITED (-1) to copy
* until the end of the input stream.
* @param callback optional callback interface.
* @return int The number of bytes copied.
* @throws java.io.IOException When the stream could not be copied.
*/
public static int copy(InputStream in, OutputStream out, int bufsz, int limit, Callback callback) throws IOException {
if (bufsz <= 0)
throw new IllegalArgumentException("Buffer size must be > 0");
byte[] buf = new byte[bufsz];
int bytesRead;
int total = 0;
int readLimit = bufsz;
_start(callback);
while (true) {
// If a limit was specified, calculate the number of bytes
// that should be read by the next read operation.
if (limit > 0) {
readLimit = limit - total;
if (readLimit > bufsz)
readLimit = bufsz;
else if (readLimit <= 0)
break;
}
bytesRead = in.read(buf, 0, readLimit);
if (bytesRead == EOS)
break;
total += bytesRead;
if (callback != null) {
boolean keepGoing = callback.beforeWrite(buf, bytesRead, total);
if (!keepGoing)
break;
}
if (out != null)
out.write(buf, 0, bytesRead);
} // while
_end(callback, total);
return total;
}
private static void _end(Callback callback, int total) {
if (callback != null)
callback.endOfStream(total);
}
private static void _start(Callback callback) {
if (callback != null)
callback.startOfStream();
}
/**
* Copies the input stream (reader) into the output stream (writer) in an efficient manner.
* This version does not synchronize on the streams, so it is not safe
* to use when the streams are being accessed by multiple threads.
*
* @param in The input reader
* @param out The output writer. If this is null, the input will be
* discarded, similar to piping to /dev/null on UN*X.
* @param bufsz The size of the buffer to use.
* @param limit The number of bytes to copy, or UNLIMITED (-1) to copy
* until the end of the input stream.
* @return int The number of bytes copied.
* @throws java.io.IOException When the stream could not be copied.
*/
public static int copy(Reader in, Writer out, int bufsz, int limit) throws IOException {
return copy(in,out,bufsz,limit,NO_CALLBACK);
}
/**
* Copies the input stream (reader) into the output stream (writer) in an efficient manner.
* This version does not synchronize on the streams, so it is not safe
* to use when the streams are being accessed by multiple threads.
*
* @param in The input reader
* @param out The output writer. If this is null, the input will be
* discarded, similar to piping to /dev/null on UN*X.
* @param bufsz The size of the buffer to use.
* @param limit The number of bytes to copy, or UNLIMITED (-1) to copy
* until the end of the input stream.
* @param callback optional callback interface.
* @return int The number of bytes copied.
* @throws java.io.IOException When the stream could not be copied.
*/
public static int copy(Reader in, Writer out, int bufsz, int limit,Callback callback) throws IOException {
if (bufsz <= 0)
throw new IllegalArgumentException("Buffer size must be > 0");
char[] buf = new char[bufsz];
int bytesRead;
int total = 0;
int readLimit = bufsz;
_start(callback);
while (true) {
// If a limit was specified, calculate the number of bytes
// that should be read by the next read operation.
if (limit > 0) {
readLimit = limit - total;
if (readLimit > bufsz)
readLimit = bufsz;
else if (readLimit <= 0)
break;
}
bytesRead = in.read(buf, 0, readLimit);
if (bytesRead == EOS)
break;
total += bytesRead;
if (callback != null) {
boolean keepGoing = callback.beforeWrite(buf, bytesRead, total);
if (!keepGoing)
break;
}
if (out != null)
out.write(buf, 0, bytesRead);
} // while
_end(callback,total);
return total;
}
/**
* Reads the entire input stream into a byte array.
*
* @param in The input stream
* @return The contents of the input stream as a byte array.
* @throws IOException if something goes wrong while copying.
*/
public static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(in, baos, Copier.DEFAULT_BUFFER_SIZE, UNLIMITED, NO_CALLBACK);
return baos.toByteArray();
}
}