//============================================================================== // Base64EncoderInputStream.java //============================================================================== package tribble.io; // System imports import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.IllegalArgumentException; import java.lang.String; // Local imports import tribble.util.Base64Encoder; /******************************************************************************* * Base-64 encoding input 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 read from * the input stream. * *
* See {@link tribble.util.Base64Encoder} for more information about base-64
* encoding.
*
*
* @version $Revision: 1.1 $ $Date: 2005/04/14 03:39:07 $
* @since 2005-04-13
* @author
* David R. Tribble
* (david@tribble.com).
*
*
* Copyright ©2005 by David R. Tribble, all rights reserved.
*
* Permission is granted to freely use and distribute this source
* code provided that the original copyright and authorship notices
* remain intact.
*
* @see Base64DecoderInputStream
* @see Base64EncoderOutputStream
* @see tribble.util.Base64Encoder
*/
public class Base64EncoderInputStream
extends java.io.FilterInputStream
{
// Identification
/** Revision information. */
static final String REV =
"@(#)tribble/io/Base64EncoderInputStream.java $Revision: 1.1 $ $Date: 2005/04/14 03:39:07 $\n";
/** Class revision number. */
public static final int SERIES = 100;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Public 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;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Protected constants
/** 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.Base64EncoderInputStream * [-o outfile] file... * * * * @param args * Command line arguments. * * @throws IOException * Thrown if an I/O error occurs. * * @since 1.1, 2005-04-11 */ public static void main(String[] args) throws IOException { String outFname = "-"; OutputStream os; OutputStream out; byte[] buf; 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 binary input files and convert them to " + "base-64 character form."); System.out.println(); System.out.println("usage: java " + Base64EncoderInputStream.class.getName() + " [-o outfile] file..."); System.exit(255); } // Set up the output stream if (outFname.equals("-")) { // Write output to stdout os = new BufferedOutputStream(System.out); } else { // Write output to a named file os = new FileOutputStream(outFname); } out = new BufferedOutputStream(os); // Read one or more input files buf = new byte[64*1024]; for ( ; i < args.length; i++) { String fname; InputStream in; // Read a named input file fname = args[i]; if (fname.equals("-")) { // Read input from stdin in = new Base64EncoderInputStream(System.in); } else { // Read a named input file in = new Base64EncoderInputStream(new FileInputStream(fname)); } // Read and convert the input file into base-64 System.err.println(fname); for (;;) { int len; // Read and convert a block of input bytes len = in.read(buf); if (len < 0) break; out.write(buf, 0, len); } // Clean up out.flush(); if (!fname.equals("-")) in.close(); } // Clean up if (!outFname.equals("-")) out.close(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected variables /** Pending input queue. */ protected byte[] m_inBuf = new byte[3*16]; /** Output queue. */ protected byte[] m_outBuf = new byte[4*16 + NEWLINE.length]; /** Pending input queue length. */ protected short m_inLen; /** Next output byte queue index. */ protected short m_outPos; /** Current output byte queue length. */ protected short m_outLen; /** Newline output style. */ protected short m_newlineStyle = NEWLINE_NATIVE; /** End of input stream reached. */ protected boolean m_eof; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public constructors /*************************************************************************** * Constructor. * * @param in * The underlying input stream for this input stream. * * @since 1.1, 2005-04-11 */ public Base64EncoderInputStream(InputStream in) { // Establish the underlying input stream super(in); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /*************************************************************************** * Close this input stream. * *
* Any pending base-64 character input is discarded. * *
* Subsequent attempts to read from this input stream will cause exceptions * to be thrown. * * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2005-04-11 */ public void close() throws IOException //overrides java.io.InputStream { if (this.in == null) return; // Close the underlying input stream this.in.close(); // Disassociate the underlying input stream this.in = null; // Clean up m_inBuf = null; m_outBuf = null; } /*************************************************************************** * Read a character from this input stream. * *
* An 8-bit binary code (octet) is converted into its corresponding base-64 * character encoding after being read from the underlying input stream. * * * @return * An ASCII base-64 character code, or -1 if the end of the input stream has * been reached. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2005-04-11 */ public int read() throws IOException //overrides java.io.InputStream { // Ensure that there are converted bytes in the output queue if (m_outPos >= m_outLen) { // Read and convert the next input data group readPending(); } // Extract the next byte from the converted output queue if (m_outPos < m_outLen) { int b; // Pop the next character byte from the output queue b = m_outBuf[m_outPos++] & 0xFF; return (b); } else { // No more input bytes to convert return (-1); } } /*************************************************************************** * Reads up to len bytes of data from this input stream into an * array of bytes. * This method blocks until some input is available. * * * @param buf * The buffer into which the data is read. * Each 8-bit binary code (octet) is converted into its corresponding base-64 * character encoding after being read from the underlying input stream. * * @param off * The start offset within buf to read data into. * * @param len * The maximum number of bytes read into buf. * * @return * The total number of bytes read into the array, or -1 if there is no more * data because the end of the stream has been reached. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2005-04-11 */ public int read(byte[] buf, int off, int len) throws IOException //overrides java.io.InputStream { int n; // Read multiple bytes from the input stream len += off; n = off; while (n < len) { byte b; // Ensure that there are converted bytes in the output queue if (m_outPos >= m_outLen) { // Read and convert the next input data line readPending(); } // Extract the next byte from the converted output queue if (m_outPos < m_outLen) { // Get the next byte from the output queue b = m_outBuf[m_outPos++]; } else { // No more converted output bytes, end of the input stream if (n == off) return (-1); break; } // Append the converted output byte to the read buffer buf[n++] = b; } // Return the number of bytes written to the read buffer return (n - off); } /*************************************************************************** * Retrieve the underlying input stream for this output stream. * *
* Note that this method is not specified by the standard * {@link java.io.FilterInputStream} library class, but is provided as a * convenient enhancement. * * * @return * The underlying input stream for this stream, or null if it has been * closed. * * @since 1.1, 2005-04-11 */ public InputStream getInput() { // Get the input stream return (this.in); } /*************************************************************************** * Establish the handling of newline sequences for this output stream. * *
* This method provides a means for "prettying up" the character input of * this stream. * *
* The default style is {@link #NEWLINE_NATIVE}. * * * @param style * Specifies the way that newlines are inserted into the input stream. * This value must be one of the {@link #NEWLINE_NONE NEWLINE_XXX} * constants: * *
* Note that this method should only be called if {@link #m_outPos} is * greater than {@link #m_outLen}, indicating that the output queue has been * exhausted. * * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2005-04-11 */ protected void readPending() throws IOException { int n; // Fill the input buffer with 8-bit data bytes (octets) while (m_inLen < 3*16) { int b; // Read a data byte from the input stream b = this.in.read(); if (b < 0) { // End of input stream reached m_eof = true; break; } // Append the data byte to the input queue m_inBuf[m_inLen++] = (byte) b; } // Convert groups of four 6-bit sextets into three 8-bit octets m_outLen = 0; m_outPos = 0; if (m_eof) n = m_inLen; else n = m_inLen/3 * 3; if (n > 0) { // Convert groups of 8-bit octets into base-64 sextets m_outLen = (short) Base64Encoder.encodeAsBytes(m_inBuf, 0, n, m_outBuf, 0); // Add a terminating newline sequence switch (m_newlineStyle) { case NEWLINE_BYTE: // Append a single newline character m_outBuf[m_outLen++] = (byte) '\n'; break; case NEWLINE_NATIVE: // Append a native newline character sequence for (int i = 0; i < NEWLINE.length; i++) m_outBuf[m_outLen++] = NEWLINE[i]; break; case NEWLINE_NONE: default: // Do not append anything break; } // Save the leftover partial octet group, if any m_inLen -= n; if (m_inLen > 0) { int i, j; for (i = 0, j = n; i < m_inLen; i++, j++) m_inBuf[i] = m_inBuf[j]; } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private constructors /*************************************************************************** * Default constructor. * Do not use this constructor. * * @since 1.1, 2005-04-11 */ private Base64EncoderInputStream() { // Do nothing super(null); } } // End Base64EncoderInputStream.java