//============================================================================== // Base64EncoderOutputStream.java //============================================================================== package tribble.io; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import java.lang.IllegalArgumentException; import java.lang.IndexOutOfBoundsException; import java.lang.NullPointerException; import java.lang.String; import tribble.util.Base64Encoder; import tribble.io.WriterOutputStream; /******************************************************************************* * Base-64 encoding output stream. * *
* Provides an I/O stream that encodes binary data as base-64 characters. * 8-bit bytes (octets) are converted into printable 8-bit characters that encode * the data as base-64 (a.k.a. radix-64) ASCII characters as they are written to * the output stream. * *
* See {@link tribble.util.Base64Encoder} for more information about base-64 * encoding. * * *
* Copyright ©2003-2009 by David R. Tribble, all rights reserved.
* Permission is granted to any person or entity except those designated by
* by the United States Department of State as a terrorist, or terrorist
* government or agency, to use and distribute this source code provided
* that the original copyright notice remains present and unaltered.
*
* @see Base64EncoderInputStream
* @see Base64DecoderOutputStream
* @see tribble.util.Base64Encoder
*/
public class Base64EncoderOutputStream
extends java.io.FilterOutputStream
{
static final String REV =
"@(#)tribble/io/Base64EncoderOutputStream.java $Revision: 1.9 $ $Date: 2009/02/04 03:35:42 $\n";
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Constants
//----------------------------------
// Newline output styles
/** Newline output style: No newline sequences. */
public static final short NEWLINE_NONE = 0;
/** Newline output style: Single byte ('\n'). */
public static final short NEWLINE_BYTE = 1;
/** Newline output style: Native platform character sequence. */
public static final short NEWLINE_NATIVE = 2;
/** Newline output style: Call out.newLine(). */
protected static final short NEWLINE_CALL = 3;
/** Platform-specific newline character sequence. */
protected static final byte[] NEWLINE =
System.getProperty("line.separator").getBytes();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Static methods
/***************************************************************************
* Test driver.
* Read one or more input files and write them in base-64 output form.
*
*
* Usage *
* * java tribble.io.Base64EncoderOutputStream * [-o outfile] file... * * * @param args * Command line arguments. * * @throws IOException * Thrown if an I/O (write) error occurs. * * @since 1.2, 2005-04-01 */ public static void main(String[] args) throws IOException { Base64EncoderOutputStream out; String outFname = "-"; OutputStream os; int rc = 0; int i; // Parse command options for (i = 0; i < args.length && args[i].charAt(0) == '-'; i++) { if (args[i].equals("-o")) outFname = args[++i]; else if (args[i].equals("-")) break; else throw new IOException("Bad option: '" + args[i] + "'"); } // Check args if (i >= args.length) { // Show command usage System.out.println("Read input files and write them in base-64 " + "output form."); System.out.println(); System.out.println("usage: java " + Base64EncoderOutputStream.class.getName() + " [-o outfile] file..."); System.exit(255); } // Set up the output stream if (outFname.equals("-")) os = new BufferedOutputStream(System.out); else os = new BufferedOutputStream(new FileOutputStream(outFname)); out = new Base64EncoderOutputStream(os); // Read one or more input files for ( ; i < args.length; i++) { String inFname = null; try { InputStream in; // Read a named input file inFname = args[i]; if (inFname.equals("-")) in = System.in; else in = new FileInputStream(inFname); // Read and convert the input file into base-64 characters System.err.println(inFname); out.encode(in); // Clean up if (!inFname.equals("-")) in.close(); } catch (IOException ex) { System.err.println("Can't convert: " + inFname); System.err.println(ex.getMessage()); rc = 1; } } // Clean up out.finish(); out.flush(); if (!outFname.equals("-")) out.close(); System.exit(rc); } /*************************************************************************** * Read and convert the contents of an input stream. * This method is equivalent to the call: * {@link #encode(InputStream, OutputStream, int) encode}(in, out, 0). * * @param in * An input stream containing 8-bit octet data to be converted into base-64 * character output. * * @param out * An output stream to which the converted base-64 character output is to be * written. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if in or out is null. * * @see #encode(InputStream) encode() * * @since 1.9, 2009-02-03 */ public static void encode(InputStream in, OutputStream out) throws IOException { // Read and convert the input stream into base-64 characters, writing // the encoded characters to the output stream encode(in, out, 0); } /*************************************************************************** * Read and convert the contents of an input stream. * *
* The entire contents of an input stream are read as 8-bit octets and * converted into base-64 encoded characters, which are then written to an * output stream. * *
* Note that the output stream is finished and flushed after the converted * data is written, but is not closed, i.e., methods {@link #finish finish()} * and {@link #flush flush()} are called but {@link #close close()} is not. * * @param in * An input stream containing 8-bit octet data to be converted into base-64 * character output. * * @param out * An output stream to which the converted base-64 character output is to be * written. * * @param bufLen * Specifies the size, in bytes, of the internal buffer to use for reading * data from the input stream. Specifying a value less than 3 causes a * default size of 64K (65,536) to be used. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if in or out is null. * * @since 1.9, 2009-02-03 */ public static void encode(InputStream in, OutputStream out, int bufLen) throws IOException { Base64EncoderOutputStream enc; // Sanity check if (out == null) throw new NullPointerException(); // Read and convert the input stream into base-64 characters, writing // the encoded characters to the output stream enc = new Base64EncoderOutputStream(out); enc.encode(in, bufLen); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Variables /** Pending input queue. */ protected byte[] m_inBuf = new byte[3*16]; /** Output queue. */ protected byte[] m_outBuf = new byte[4*16]; /** Pending input queue length. */ protected short m_inLen; /** Number of bytes from the output line written to the output. */ protected short m_outLen; /** Newline output style. */ protected short m_newlineStyle = NEWLINE_NATIVE; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors /*************************************************************************** * Constructor. * * @param out * The underlying output stream for this output stream. * * @throws NullPointerException (unchecked) * Thrown if out is null. * * @since 1.4, 2005-04-08 */ public Base64EncoderOutputStream(OutputStream out) { // Establish the underlying output stream super(out); // Sanity check if (out == null) throw new NullPointerException(); } /*************************************************************************** * Constructor. * * @param out * The underlying output writer for this output stream. * * @throws NullPointerException (unchecked) * Thrown if out is null. * * @since 1.5, 2005-04-08 */ public Base64EncoderOutputStream(Writer out) { // Establish the underlying output stream super(new WriterOutputStream(out)); // Sanity check if (out == null) throw new NullPointerException(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Methods /*************************************************************************** * Complete all pending output to this output stream. * *
* All pending base-64 character output is completed, and final padding * characters (if any) are appended to the underlying output stream. * * @throws IOException * Thrown if an I/O (write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if the underlying output stream is closed. * * @see #flush * * @since 1.7, 2005-07-18 */ public void finish() throws IOException { // Sanity check if (this.out == null) throw new NullPointerException(); // Write any pending base-64 characters to the output if (m_inLen > 0) { // Convert and write 8-bit input octets as four 6-bit sextets, // appending trailing '=' padding characters if necessary writePending(m_inLen); } // Write a final newline if (m_outLen > 0) { newLine(); m_outLen = 0; } // Note: Do not flush or close the underlying output stream } /*************************************************************************** * Flush all pending output to this output stream. * *
* Any pending base-64 character output is written to the underlying output * stream, then the underlying output stream itself is flushed. * *
* Note that output characters may still remain pending after this operation * if the last bytes written to this stream do not constitute a complete * 24-bit octet group. Use the {@link #finish finish()} method to force * completion of the output stream. * * @throws IOException * Thrown if an I/O (write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if the underlying output stream is closed. * * @see #finish * * @since 1.1, 2005-04-01 */ public void flush() throws IOException //overrides java.io.OutputStream { // Sanity check if (this.out == null) throw new NullPointerException(); // Write pending base-64 characters to the output if (m_inLen >= 3) { // Convert and write complete 8-bit octet groups as 6-bit sextets, // leaving any leftover partial octet as pending output writePending(m_inLen/3 * 3); } // Flush the underlying output stream this.out.flush(); } /*************************************************************************** * Flush and close this output stream. * *
* Any pending base-64 character output is completed, and the final padding * characters (if any) are written to the underlying output stream. * *
* Subsequent attempts to write to this output stream will cause exceptions * to be thrown. * * @throws IOException * Thrown if an I/O (write) error occurs. * * @see #finish * @see #flush * * @since 1.1, 2003-02-26 */ public void close() throws IOException //overrides java.io.OutputStream { // Sanity check if (this.out == null) return; // Flush all pending output to the underlying output stream finish(); flush(); // Clean up m_inBuf = null; m_outBuf = null; // Close and disassociate the underlying output stream this.out.close(); this.out = null; } /*************************************************************************** * Write a byte to this output stream. * *
* The 8-bit byte is converted into its corresponding base-64 character * encoding before being written to the underlying output stream. * * @param c * A character code. * Note that only the lower 8 bits are written to the underlying output * stream, and the upper 24 bits are ignored. * * @throws IOException * Thrown if an I/O (write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if the underlying output stream is closed. * * @since 1.1, 2003-02-26 */ public void write(int c) throws IOException //overrides java.io.OutputStream { // Sanity check if (this.out == null) throw new NullPointerException(); // Append the output byte to the output queue m_inBuf[m_inLen++] = (byte) c; if (m_inLen >= 3*16) { // Convert and write three 8-bit octets as four 6-bit sextets writePending(m_inLen); } } /*************************************************************************** * Write a set of bytes to this output stream. * *
* The 8-bit bytes are converted into their corresponding base-64 character * encoding before being written to the underlying output stream. * * @param data * An array of bytes to write. * * @param off * Index of the first byte within data to write. * * @param len * Number of bytes from data to write. * * @throws IOException * Thrown if an I/O (write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if the underlying output stream is closed. * * @throws IndexOutOfBoundsException (unchecked) * Thrown if off or len specify a negative offset or * length. * * @since 1.1, 2003-02-26 */ public void write(byte[] data, int off, int len) throws IOException //overrides java.io.OutputStream { // Sanity checks if (this.out == null) throw new NullPointerException(); if ((off | len | (off + len)) < 0) throw new IndexOutOfBoundsException(); if (data.length - (off + len) < 0) throw new IndexOutOfBoundsException(); // Append the output bytes to the output queue len += off; for (int i = off; i < len; i++) { m_inBuf[m_inLen++] = (byte) data[i]; if (m_inLen >= 3*16) { // Convert and write three 8-bit octets as four 6-bit sextets writePending(m_inLen); } } } /*************************************************************************** * Retrieve the underlying output stream for this output stream. * *
* Note that this method is not specified by the standard * {@link java.io.FilterOutputStream} library class, but is provided as a * convenient enhancement. * * @return * The underlying output stream for this stream, or null if it has been * closed. * * @since 1.2, 2005-04-01 */ public OutputStream getOutput() { // Get the output stream return this.out; } /*************************************************************************** * Establish the handling of newline sequences for this output stream. * *
* Note that newline character sequences are not encoded as base-64 * character codes, but is simply written as is to the underlying output * stream. Thus this method provides a means for "prettying up" the * character output of this stream. * *
* The default style is {@link #NEWLINE_NATIVE}. * * @param style * Specifies the way that newlines are written to the output stream. * This value must be one of the {@link #NEWLINE_NONE NEWLINE_XXX} * constants: * *
* The behavior of this method depends on the current newline output style * setting, which was set by the last call to * {@link #setNewline setNewline()}. * *
* If the underlying output stream is a {@link java.io.Writer} object and the * newline output mode is set to {@link #NEWLINE_NATIVE}, then the * appropriate method of the output writer is called. If the writer is a * {@link java.io.PrintWriter}, its println() method is called; if * it is a {@link java.io.BufferedWriter}, its newLine() method is * called; otherwise, the native newline character sequence is written to it. * *
* Note that the newline character sequence is not encoded as base-64 * character codes, but is simply written as is to the underlying output * stream. Thus this method provides a means for "prettying up" the * character output of this stream. * * @throws IOException * Thrown if an I/O (write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if the underlying output stream is closed. * * @since 1.1, 2003-02-26 */ public void newLine() throws IOException { // Sanity check if (this.out == null) throw new NullPointerException(); // Write a newline sequence to the underlying output switch (m_newlineStyle) { case NEWLINE_BYTE: // Write a single newline character byte this.out.write('\n'); break; case NEWLINE_NATIVE: // Write the native newline character sequence this.out.write(NEWLINE, 0, NEWLINE.length); break; case NEWLINE_CALL: // Call out.newLine() to write the newline ((WriterOutputStream) this.out).newLine(); break; case NEWLINE_NONE: default: // No output written break; } } /*************************************************************************** * Read and convert the contents of an input stream. * This method is equivalent to the call: * {@link #encode(InputStream, int) encode}(in, 0). * * @param in * An input stream containing 8-bit octet data to be converted into base-64 * character output. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if in is null. * * @see #encode(InputStream, int) encode() * * @since 1.6, 2005-04-16 */ public void encode(InputStream in) throws IOException { // Read and convert the input stream into base-64 characters encode(in, 0); } /*************************************************************************** * Read and convert the contents of an input stream. * *
* The entire contents of an input stream are read as 8-bit octets and * converted into base-64 encoded characters, which are then written to this * output stream. * *
* Note that the output stream is finished and flushed after the converted * data is written, but is not closed, i.e., methods {@link #finish finish()} * and {@link #flush flush()} are called but {@link #close close()} is not. * * @param in * An input stream containing 8-bit octet data to be converted into base-64 * character output. * * @param bufLen * Specifies the size, in bytes, of the internal buffer to use for reading * data from the input stream. Specifying a value less than 3 causes a * default size of 64K (65,536) to be used. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if in is null. * * @since 1.6, 2005-04-16 */ public void encode(InputStream in, int bufLen) throws IOException { byte[] buf; // Sanity check if (in == null) throw new NullPointerException(); // Allocate an input buffer if (bufLen < 3) bufLen = 64*1024; buf = new byte[bufLen]; // Read and convert the input stream into base-64 characters for (;;) { int len; // Read and convert a block of input bytes len = in.read(buf); if (len < 0) break; // Write the converted base-64 characters to the output stream write(buf, 0, len); } // Clean up finish(); flush(); } /*************************************************************************** * Convert pending input data bytes into base-64 character bytes and write * them to the output stream. * Final padding characters, if necessary, are appended to the underlying * output stream. * * @param len * The number of bytes to convert and write from the pending output buffer * ({@link #m_inBuf}). This should not be greater than the current value of * {@link #m_inLen}. * * @throws IOException * Thrown if an I/O (write) error occurs. * * @since 1.7, 2005-07-18 */ protected void writePending(int len) throws IOException { int n; // Convert groups of three 8-bit octets into four 6-bit sextets n = Base64Encoder.encodeAsBytes(m_inBuf, 0, len, m_outBuf, 0); // Write the converted sextets this.out.write(m_outBuf, 0, n); m_outLen += n; // Save any leftover unconverted partial 8-bit octet as pending output m_inLen -= len; if (m_inLen > 0) { n = 0; while (n < m_inLen) m_inBuf[n++] = m_inBuf[len++]; } // Write a newline after each output line if (m_outLen > 4*16-4) { newLine(); m_outLen = 0; } } } // End Base64EncoderOutputStream.java