001    /*
002     * Java Base64 - A pure Java library for reading and writing Base64
003     *               encoded streams.
004     * 
005     * Copyright (C) 2007-2009 Carlo Pelliccia (www.sauronsoftware.it)
006     * 
007     * This program is free software: you can redistribute it and/or modify
008     * it under the terms of the GNU Lesser General Public License version
009     * 2.1, as published by the Free Software Foundation.
010     *
011     * This program is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014     * GNU General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License version 2.1 along with this program.
018     * If not, see <http://www.gnu.org/licenses/>.
019     */
020    package it.sauronsoftware.base64;
021    
022    import java.io.IOException;
023    import java.io.OutputStream;
024    
025    /**
026     * <p>
027     * A base64 decoding output stream.
028     * </p>
029     * 
030     * <p>
031     * It encodes in base64 everything passed to the stream, and it puts the encoded
032     * data into the underlying stream.
033     * </p>
034     * 
035     * @author Carlo Pelliccia
036     */
037    public class Base64OutputStream extends OutputStream {
038    
039            /**
040             * The underlying stream.
041             */
042            private OutputStream outputStream = null;
043    
044            /**
045             * A value buffer.
046             */
047            private int buffer = 0;
048    
049            /**
050             * How many bytes are currently in the value buffer?
051             */
052            private int bytecounter = 0;
053    
054            /**
055             * A counter for the current line length.
056             */
057            private int linecounter = 0;
058    
059            /**
060             * The requested line length.
061             */
062            private int linelength = 0;
063    
064            /**
065             * <p>
066             * It builds a base64 encoding output stream writing the encoded data in the
067             * given underlying stream.
068             * </p>
069             * 
070             * <p>
071             * The encoded data is wrapped to a new line (with a CRLF sequence) every 76
072             * bytes sent to the underlying stream.
073             * </p>
074             * 
075             * @param outputStream
076             *            The underlying stream.
077             */
078            public Base64OutputStream(OutputStream outputStream) {
079                    this(outputStream, 76);
080            }
081    
082            /**
083             * <p>
084             * It builds a base64 encoding output stream writing the encoded data in the
085             * given underlying stream.
086             * </p>
087             * 
088             * <p>
089             * The encoded data is wrapped to a new line (with a CRLF sequence) every
090             * <em>wrapAt</em> bytes sent to the underlying stream. If the
091             * <em>wrapAt</em> supplied value is less than 1 the encoded data will not
092             * be wrapped.
093             * </p>
094             * 
095             * @param outputStream
096             *            The underlying stream.
097             * @param wrapAt
098             *            The max line length for encoded data. If less than 1 no wrap
099             *            is applied.
100             */
101            public Base64OutputStream(OutputStream outputStream, int wrapAt) {
102                    this.outputStream = outputStream;
103                    this.linelength = wrapAt;
104            }
105    
106            public void write(int b) throws IOException {
107                    int value = (b & 0xFF) << (16 - (bytecounter * 8));
108                    buffer = buffer | value;
109                    bytecounter++;
110                    if (bytecounter == 3) {
111                            commit();
112                    }
113            }
114    
115            public void close() throws IOException {
116                    commit();
117                    outputStream.close();
118            }
119    
120            /**
121             * <p>
122             * It commits 4 bytes to the underlying stream.
123             * </p>
124             */
125            protected void commit() throws IOException {
126                    if (bytecounter > 0) {
127                            if (linelength > 0 && linecounter == linelength) {
128                                    outputStream.write("\r\n".getBytes());
129                                    linecounter = 0;
130                            }
131                            char b1 = Shared.chars.charAt((buffer << 8) >>> 26);
132                            char b2 = Shared.chars.charAt((buffer << 14) >>> 26);
133                            char b3 = (bytecounter < 2) ? Shared.pad : Shared.chars.charAt((buffer << 20) >>> 26);
134                            char b4 = (bytecounter < 3) ? Shared.pad : Shared.chars.charAt((buffer << 26) >>> 26);
135                            outputStream.write(b1);
136                            outputStream.write(b2);
137                            outputStream.write(b3);
138                            outputStream.write(b4);
139                            linecounter += 4;
140                            bytecounter = 0;
141                            buffer = 0;
142                    }
143            }
144    
145    }