/******************************************************************************* * $Id$ * $Author$ * $Date$ * * Copyright 2002 - YAJUL Developers, Joshua Davis, Kent Vogel. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * ******************************************************************************/ /** * Created by IntelliJ IDEA. * User: josh * Date: Nov 27, 2002 * Time: 8:43:37 AM */ // --- The orgiginal code is from: --- // Base64Encoder.java // $Id$ // (c) COPYRIGHT MIT and INRIA, 1996. // Please first read the full copyright statement in file COPYRIGHT.html package org.yajul.io; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Provides BASE64 encoding of binary data as an output stream filter. Bytes * written to this output stream filter will be encoded using the BASE64 * encoding rules, as defined in * <a href="http://ds.internic.net/rfc/rfc1521.txt">MIME specification</a> * and written to the underlying output stream. * * @author josh (Refactored from old 'Intira' code) */ public class Base64OutputStream extends FilterOutputStream { /** A logger for this class. */ // private static Logger log = Logger.getLogger(Base64OutputStream.class); /** * The number of BASE64 encoded bytes to put in a 'block'. * */ private static final int BLOCK_SIZE = 76; /** * The encoding matrix. The index is the 'input' binary value, and * the value is the BASE64 encoded representation. * */ private static final byte ENCODED[] = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', // 0-7 (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', // 8-15 (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', // 16-23 (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', // 24-31 (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', // 32-39 (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', // 40-47 (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', // 48-55 (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/', // 56-63 (byte) '=' // 64 }; /** * Keeps track of the BASE64 state (byte number) in the input stream. * */ private int state; /** * Keeps track of the number of bytes written so far, used to add a * newline every 76 encoded bytes. * */ private int count; /** * The previous encoded and parsed byte. * */ private int previous; /** * Creates a new BASE64 encoding output stream. * * @param out The underlying output stream. */ public Base64OutputStream(OutputStream out) { super(out); state = 0; count = 0; previous = 0; } /** * Writes the specified <code>byte</code> to this output stream. * <p/> * The <code>write</code> method of <code>FilterOutputStream</code> * calls the <code>write</code> method of its underlying output stream, * that is, it performs <tt>out.write(b)</tt>. * <p/> * Implements the abstract <tt>write</tt> method of <tt>OutputStream</tt>. * * @param b the <code>byte</code>. * @throws java.io.IOException if an I/O error occurs. */ public void write(int b) throws IOException { state++; byte inbyte = (byte) b; byte prevByte = (byte) previous; switch (state) { case 1: writeByte(ENCODED[get1(inbyte)]); previous = b; break; case 2: writeByte(ENCODED[get2(prevByte, inbyte)]); previous = b; break; case 3: writeByte(ENCODED[get3(prevByte, inbyte)]); writeByte(ENCODED[get4(inbyte)]); state = 0; break; default: throw new IllegalStateException("Unknown state: " + state); } } /** * Flushes this output stream and forces any buffered output bytes * to be written out to the stream. * <p/> * The <code>flush</code> method of <code>FilterOutputStream</code> * calls the <code>flush</code> method of its underlying output stream. * * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterOutputStream#out */ public void flush() throws IOException { switch (state) { /* 1 2 3 in: xx123456 xx781234 xx567812 xx345678 out: 12345678 12345678 12345678 12345678 1 2 3 4 Input byte 1 -> output bytes 1 & 2 Input byte 2 -> output bytes 2 & 3 Input byte 3 -> output bytes 3 & 4 */ case 1: // First output byte written, so write the second // output byte with the last two bits of the first input // byte in it. writeByte(ENCODED[get2((byte) previous, (byte) 0)]); super.write('='); super.write('='); break; case 2: // First and second bytes written, so write the third output // byte with bits 5-8 of the second input byte. writeByte(ENCODED[get3((byte) previous, (byte) 0)]); super.write('='); break; case 3: // Input length mod 3 is zero, so no padding. break; } super.flush(); } /** * Writes the byte to the encoded output stream, inserting '\n' characters * to separate the blocks (as per the BASE64 spec). * * @param b The encoded byte to write. * @throws IOException if something goes wrong. */ private void writeByte(int b) throws IOException { count++; if (count > BLOCK_SIZE) { super.write('\n'); count = 0; } super.write(b); } private static int get1(byte byte1) { return (byte1 & 0xfc) >> 2; } private static int get2(byte byte1, byte byte2) { return ((byte1 & 0x3) << 4) | ((byte2 & 0xf0) >>> 4); } private static int get3(byte byte2, byte byte3) { return ((byte2 & 0x0f) << 2) | ((byte3 & 0xc0) >>> 6); } private static int get4(byte byte3) { return byte3 & 0x3f; } }