source: contrib/MailArchiver/sources/vendor/mime4j/apache-mime4j-0.7-SNAPSHOT-20110327.010440-17/core/src/main/java/org/apache/james/mime4j/codec/Base64OutputStream.java @ 6785

Revision 6785, 10.5 KB checked in by rafaelraymundo, 12 years ago (diff)

Ticket #2946 - Liberado codigo do MailArchiver?. Documentação na subpasta DOCS.

Line 
1/****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one   *
3 * or more contributor license agreements.  See the NOTICE file *
4 * distributed with this work for additional information        *
5 * regarding copyright ownership.  The ASF licenses this file   *
6 * to you under the Apache License, Version 2.0 (the            *
7 * "License"); you may not use this file except in compliance   *
8 * with the License.  You may obtain a copy of the License at   *
9 *                                                              *
10 *   http://www.apache.org/licenses/LICENSE-2.0                 *
11 *                                                              *
12 * Unless required by applicable law or agreed to in writing,   *
13 * software distributed under the License is distributed on an  *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15 * KIND, either express or implied.  See the License for the    *
16 * specific language governing permissions and limitations      *
17 * under the License.                                           *
18 ****************************************************************/
19
20package org.apache.james.mime4j.codec;
21
22import java.io.FilterOutputStream;
23import java.io.IOException;
24import java.io.OutputStream;
25import java.util.HashSet;
26import java.util.Set;
27
28/**
29 * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite>
30 * from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One:
31 * Format of Internet Message Bodies</cite> by Freed and Borenstein.
32 * <p>
33 * Code is based on Base64 and Base64OutputStream code from Commons-Codec 1.4.
34 *
35 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
36 */
37public class Base64OutputStream extends FilterOutputStream {
38
39    // Default line length per RFC 2045 section 6.8.
40    private static final int DEFAULT_LINE_LENGTH = 76;
41
42    // CRLF line separator per RFC 2045 section 2.1.
43    private static final byte[] CRLF_SEPARATOR = { '\r', '\n' };
44
45    // This array is a lookup table that translates 6-bit positive integer index
46    // values into their "Base64 Alphabet" equivalents as specified in Table 1
47    // of RFC 2045.
48    static final byte[] BASE64_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F',
49            'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
50            'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
51            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
52            't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
53            '6', '7', '8', '9', '+', '/' };
54
55    // Byte used to pad output.
56    private static final byte BASE64_PAD = '=';
57
58    // This set contains all base64 characters including the pad character. Used
59    // solely to check if a line separator contains any of these characters.
60    private static final Set<Byte> BASE64_CHARS = new HashSet<Byte>();
61
62    static {
63        for (byte b : BASE64_TABLE) {
64            BASE64_CHARS.add(b);
65        }
66        BASE64_CHARS.add(BASE64_PAD);
67    }
68
69    // Mask used to extract 6 bits
70    private static final int MASK_6BITS = 0x3f;
71
72    private static final int ENCODED_BUFFER_SIZE = 2048;
73
74    private final byte[] singleByte = new byte[1];
75
76    private final int lineLength;
77    private final byte[] lineSeparator;
78
79    private boolean closed = false;
80
81    private final byte[] encoded;
82    private int position = 0;
83
84    private int data = 0;
85    private int modulus = 0;
86
87    private int linePosition = 0;
88
89    /**
90     * Creates a <code>Base64OutputStream</code> that writes the encoded data
91     * to the given output stream using the default line length (76) and line
92     * separator (CRLF).
93     *
94     * @param out
95     *            underlying output stream.
96     */
97    public Base64OutputStream(OutputStream out) {
98        this(out, DEFAULT_LINE_LENGTH, CRLF_SEPARATOR);
99    }
100
101    /**
102     * Creates a <code>Base64OutputStream</code> that writes the encoded data
103     * to the given output stream using the given line length and the default
104     * line separator (CRLF).
105     * <p>
106     * The given line length will be rounded up to the nearest multiple of 4. If
107     * the line length is zero then the output will not be split into lines.
108     *
109     * @param out
110     *            underlying output stream.
111     * @param lineLength
112     *            desired line length.
113     */
114    public Base64OutputStream(OutputStream out, int lineLength) {
115        this(out, lineLength, CRLF_SEPARATOR);
116    }
117
118    /**
119     * Creates a <code>Base64OutputStream</code> that writes the encoded data
120     * to the given output stream using the given line length and line
121     * separator.
122     * <p>
123     * The given line length will be rounded up to the nearest multiple of 4. If
124     * the line length is zero then the output will not be split into lines and
125     * the line separator is ignored.
126     * <p>
127     * The line separator must not include characters from the BASE64 alphabet
128     * (including the padding character <code>=</code>).
129     *
130     * @param out
131     *            underlying output stream.
132     * @param lineLength
133     *            desired line length.
134     * @param lineSeparator
135     *            line separator to use.
136     */
137    public Base64OutputStream(OutputStream out, int lineLength,
138            byte[] lineSeparator) {
139        super(out);
140
141        if (out == null)
142            throw new IllegalArgumentException();
143        if (lineLength < 0)
144            throw new IllegalArgumentException();
145        checkLineSeparator(lineSeparator);
146
147        this.lineLength = lineLength;
148        this.lineSeparator = new byte[lineSeparator.length];
149        System.arraycopy(lineSeparator, 0, this.lineSeparator, 0,
150                lineSeparator.length);
151
152        this.encoded = new byte[ENCODED_BUFFER_SIZE];
153    }
154
155    @Override
156    public final void write(final int b) throws IOException {
157        if (closed)
158            throw new IOException("Base64OutputStream has been closed");
159
160        singleByte[0] = (byte) b;
161        write0(singleByte, 0, 1);
162    }
163
164    @Override
165    public final void write(final byte[] buffer) throws IOException {
166        if (closed)
167            throw new IOException("Base64OutputStream has been closed");
168
169        if (buffer == null)
170            throw new NullPointerException();
171
172        if (buffer.length == 0)
173            return;
174
175        write0(buffer, 0, buffer.length);
176    }
177
178    @Override
179    public final void write(final byte[] buffer, final int offset,
180            final int length) throws IOException {
181        if (closed)
182            throw new IOException("Base64OutputStream has been closed");
183
184        if (buffer == null)
185            throw new NullPointerException();
186
187        if (offset < 0 || length < 0 || offset + length > buffer.length)
188            throw new IndexOutOfBoundsException();
189
190        if (length == 0)
191            return;
192
193        write0(buffer, offset, offset + length);
194    }
195
196    @Override
197    public void flush() throws IOException {
198        if (closed)
199            throw new IOException("Base64OutputStream has been closed");
200
201        flush0();
202    }
203
204    @Override
205    public void close() throws IOException {
206        if (closed)
207            return;
208
209        closed = true;
210        close0();
211    }
212
213    private void write0(final byte[] buffer, final int from, final int to)
214            throws IOException {
215        for (int i = from; i < to; i++) {
216            data = (data << 8) | (buffer[i] & 0xff);
217
218            if (++modulus == 3) {
219                modulus = 0;
220
221                // write line separator if necessary
222
223                if (lineLength > 0 && linePosition >= lineLength) {
224                    // writeLineSeparator() inlined for performance reasons
225
226                    linePosition = 0;
227
228                    if (encoded.length - position < lineSeparator.length)
229                        flush0();
230
231                    for (byte ls : lineSeparator)
232                        encoded[position++] = ls;
233                }
234
235                // encode data into 4 bytes
236
237                if (encoded.length - position < 4)
238                    flush0();
239
240                encoded[position++] = BASE64_TABLE[(data >> 18) & MASK_6BITS];
241                encoded[position++] = BASE64_TABLE[(data >> 12) & MASK_6BITS];
242                encoded[position++] = BASE64_TABLE[(data >> 6) & MASK_6BITS];
243                encoded[position++] = BASE64_TABLE[data & MASK_6BITS];
244
245                linePosition += 4;
246            }
247        }
248    }
249
250    private void flush0() throws IOException {
251        if (position > 0) {
252            out.write(encoded, 0, position);
253            position = 0;
254        }
255    }
256
257    private void close0() throws IOException {
258        if (modulus != 0)
259            writePad();
260
261        // write line separator at the end of the encoded data
262
263        if (lineLength > 0 && linePosition > 0) {
264            writeLineSeparator();
265        }
266
267        flush0();
268    }
269
270    private void writePad() throws IOException {
271        // write line separator if necessary
272
273        if (lineLength > 0 && linePosition >= lineLength) {
274            writeLineSeparator();
275        }
276
277        // encode data into 4 bytes
278
279        if (encoded.length - position < 4)
280            flush0();
281
282        if (modulus == 1) {
283            encoded[position++] = BASE64_TABLE[(data >> 2) & MASK_6BITS];
284            encoded[position++] = BASE64_TABLE[(data << 4) & MASK_6BITS];
285            encoded[position++] = BASE64_PAD;
286            encoded[position++] = BASE64_PAD;
287        } else {
288            assert modulus == 2;
289            encoded[position++] = BASE64_TABLE[(data >> 10) & MASK_6BITS];
290            encoded[position++] = BASE64_TABLE[(data >> 4) & MASK_6BITS];
291            encoded[position++] = BASE64_TABLE[(data << 2) & MASK_6BITS];
292            encoded[position++] = BASE64_PAD;
293        }
294
295        linePosition += 4;
296    }
297
298    private void writeLineSeparator() throws IOException {
299        linePosition = 0;
300
301        if (encoded.length - position < lineSeparator.length)
302            flush0();
303
304        for (byte ls : lineSeparator)
305            encoded[position++] = ls;
306    }
307
308    private void checkLineSeparator(byte[] lineSeparator) {
309        if (lineSeparator.length > ENCODED_BUFFER_SIZE)
310            throw new IllegalArgumentException("line separator length exceeds "
311                    + ENCODED_BUFFER_SIZE);
312
313        for (byte b : lineSeparator) {
314            if (BASE64_CHARS.contains(b)) {
315                throw new IllegalArgumentException(
316                        "line separator must not contain base64 character '"
317                                + (char) (b & 0xff) + "'");
318            }
319        }
320    }
321}
Note: See TracBrowser for help on using the repository browser.