001    /*
002     * Java Base64 - A pure Java library for reading and writing Base64
003     *               encoded streams.
004     * 
005     * Copyright (C) 2008 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 General Public License as published by
009     * the Free Software Foundation, either version 3 of the License, or
010     * (at your option) any later version.
011     *
012     * This program is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015     * GNU General Public License for more details.
016     *
017     * You should have received a copy of the GNU General Public License
018     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
019     */
020    package it.sauronsoftware.base64;
021    
022    import java.io.ByteArrayInputStream;
023    import java.io.ByteArrayOutputStream;
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.OutputStream;
027    import java.io.UnsupportedEncodingException;
028    
029    /**
030     * <p>
031     * Base64 encoding and decoding utility methods, both for binary and textual
032     * informations.
033     * </p>
034     * 
035     * @author Carlo Pelliccia
036     * @since 1.1
037     * @version 1.2
038     */
039    public class Base64 {
040    
041            /**
042             * <p>
043             * Encodes a string.
044             * </p>
045             * <p>
046             * Before the string is encoded in Base64, it is converted in a binary
047             * sequence using the system default charset.
048             * </p>
049             * 
050             * @param str
051             *            The source string.
052             * @return The encoded string.
053             * @throws RuntimeException
054             *             If an unexpected error occurs.
055             */
056            public static String encode(String str) throws RuntimeException {
057                    byte[] bytes = str.getBytes();
058                    byte[] encoded = encode(bytes);
059                    try {
060                            return new String(encoded, "ASCII");
061                    } catch (UnsupportedEncodingException e) {
062                            throw new RuntimeException("ASCII is not supported!", e);
063                    }
064            }
065    
066            /**
067             * <p>
068             * Encodes a string.
069             * </p>
070             * <p>
071             * Before the string is encoded in Base64, it is converted in a binary
072             * sequence using the supplied charset.
073             * </p>
074             * 
075             * @param str
076             *            The source string
077             * @param charset
078             *            The charset name.
079             * @return The encoded string.
080             * @throws RuntimeException
081             *             If an unexpected error occurs.
082             * @since 1.2
083             */
084            public static String encode(String str, String charset)
085                            throws RuntimeException {
086                    byte[] bytes;
087                    try {
088                            bytes = str.getBytes(charset);
089                    } catch (UnsupportedEncodingException e) {
090                            throw new RuntimeException("Unsupported charset: " + charset, e);
091                    }
092                    byte[] encoded = encode(bytes);
093                    try {
094                            return new String(encoded, "ASCII");
095                    } catch (UnsupportedEncodingException e) {
096                            throw new RuntimeException("ASCII is not supported!", e);
097                    }
098            }
099    
100            /**
101             * <p>
102             * Decodes the supplied string.
103             * </p>
104             * <p>
105             * The supplied string is decoded into a binary sequence, and then the
106             * sequence is encoded with the system default charset and returned.
107             * </p>
108             * 
109             * @param str
110             *            The encoded string.
111             * @return The decoded string.
112             * @throws RuntimeException
113             *             If an unexpected error occurs.
114             */
115            public static String decode(String str) throws RuntimeException {
116                    byte[] bytes;
117                    try {
118                            bytes = str.getBytes("ASCII");
119                    } catch (UnsupportedEncodingException e) {
120                            throw new RuntimeException("ASCII is not supported!", e);
121                    }
122                    byte[] decoded = decode(bytes);
123                    return new String(decoded);
124            }
125    
126            /**
127             * <p>
128             * Decodes the supplied string.
129             * </p>
130             * <p>
131             * The supplied string is decoded into a binary sequence, and then the
132             * sequence is encoded with the supplied charset and returned.
133             * </p>
134             * 
135             * @param str
136             *            The encoded string.
137             * @param charset
138             *            The charset name.
139             * @return The decoded string.
140             * @throws RuntimeException
141             *             If an unexpected error occurs.
142             * @since 1.2
143             */
144            public static String decode(String str, String charset)
145                            throws RuntimeException {
146                    byte[] bytes;
147                    try {
148                            bytes = str.getBytes("ASCII");
149                    } catch (UnsupportedEncodingException e) {
150                            throw new RuntimeException("ASCII is not supported!", e);
151                    }
152                    byte[] decoded = decode(bytes);
153                    try {
154                            return new String(decoded, charset);
155                    } catch (UnsupportedEncodingException e) {
156                            throw new RuntimeException("Unsupported charset: " + charset, e);
157                    }
158            }
159    
160            /**
161             * <p>
162             * Encodes a binary sequence.
163             * </p>
164             * <p>
165             * If data are large, i.e. if you are working with large binary files,
166             * consider to use a {@link Base64OutputStream} instead of loading too much
167             * data in memory.
168             * </p>
169             * 
170             * @param bytes
171             *            The source sequence.
172             * @return The encoded sequence.
173             * @throws RuntimeException
174             *             If an unexpected error occurs.
175             * @since 1.2
176             */
177            public static byte[] encode(byte[] bytes) throws RuntimeException {
178                    return encode(bytes, 0);
179            }
180    
181            /**
182             * <p>
183             * Encodes a binary sequence, wrapping every encoded line every
184             * <em>wrapAt</em> characters. A <em>wrapAt</em> value less than 1 disables
185             * wrapping.
186             * </p>
187             * <p>
188             * If data are large, i.e. if you are working with large binary files,
189             * consider to use a {@link Base64OutputStream} instead of loading too much
190             * data in memory.
191             * </p>
192             * 
193             * @param bytes
194             *            The source sequence.
195             * @param wrapAt
196             *            The max line length for encoded data. If less than 1 no wrap
197             *            is applied.
198             * @return The encoded sequence.
199             * @throws RuntimeException
200             *             If an unexpected error occurs.
201             * @since 1.2
202             */
203            public static byte[] encode(byte[] bytes, int wrapAt)
204                            throws RuntimeException {
205                    ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
206                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
207                    try {
208                            encode(inputStream, outputStream, wrapAt);
209                    } catch (IOException e) {
210                            throw new RuntimeException("Unexpected I/O error", e);
211                    } finally {
212                            try {
213                                    inputStream.close();
214                            } catch (Throwable t) {
215                                    ;
216                            }
217                            try {
218                                    outputStream.close();
219                            } catch (Throwable t) {
220                                    ;
221                            }
222                    }
223                    return outputStream.toByteArray();
224            }
225    
226            /**
227             * <p>
228             * Decodes a binary sequence.
229             * </p>
230             * <p>
231             * If data are large, i.e. if you are working with large binary files,
232             * consider to use a {@link Base64InputStream} instead of loading too much
233             * data in memory.
234             * </p>
235             * 
236             * @param bytes
237             *            The encoded sequence.
238             * @return The decoded sequence.
239             * @throws RuntimeException
240             *             If an unexpected error occurs.
241             * @since 1.2
242             */
243            public static byte[] decode(byte[] bytes) throws RuntimeException {
244                    ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
245                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
246                    try {
247                            decode(inputStream, outputStream);
248                    } catch (IOException e) {
249                            throw new RuntimeException("Unexpected I/O error", e);
250                    } finally {
251                            try {
252                                    inputStream.close();
253                            } catch (Throwable t) {
254                                    ;
255                            }
256                            try {
257                                    outputStream.close();
258                            } catch (Throwable t) {
259                                    ;
260                            }
261                    }
262                    return outputStream.toByteArray();
263            }
264    
265            /**
266             * <p>
267             * Encodes data from the given input stream and write them in the given
268             * output stream.
269             * </p>
270             * <p>
271             * The supplied input stream is read until its end is reached, but it's not
272             * closed by this method.
273             * </p>
274             * <p>
275             * The supplied output stream is nor flushed neither closed by this method.
276             * </p>
277             * 
278             * @param inputStream
279             *            The input stream.
280             * @param outputStream
281             *            The output stream.
282             * @throws IOException
283             *             If an I/O error occurs.
284             */
285            public static void encode(InputStream inputStream, OutputStream outputStream)
286                            throws IOException {
287                    encode(inputStream, outputStream, 0);
288            }
289    
290            /**
291             * <p>
292             * Encodes data from the given input stream and write them in the given
293             * output stream, wrapping every encoded line every <em>wrapAt</em>
294             * characters. A <em>wrapAt</em> value less than 1 disables wrapping.
295             * </p>
296             * <p>
297             * The supplied input stream is read until its end is reached, but it's not
298             * closed by this method.
299             * </p>
300             * <p>
301             * The supplied output stream is nor flushed neither closed by this method.
302             * </p>
303             * 
304             * @param inputStream
305             *            The input stream from which clear data are read.
306             * @param outputStream
307             *            The output stream in which encoded data are written.
308             * @param wrapAt
309             *            The max line length for encoded data. If less than 1 no wrap
310             *            is applied.
311             * @throws IOException
312             *             If an I/O error occurs.
313             */
314            public static void encode(InputStream inputStream,
315                            OutputStream outputStream, int wrapAt) throws IOException {
316                    Base64OutputStream aux = new Base64OutputStream(outputStream);
317                    copy(inputStream, aux);
318                    aux.commit();
319            }
320    
321            /**
322             * <p>
323             * Decodes data from the given input stream and write them in the given
324             * output stream.
325             * </p>
326             * <p>
327             * The supplied input stream is read until its end is reached, but it's not
328             * closed by this method.
329             * </p>
330             * <p>
331             * The supplied output stream is nor flushed neither closed by this method.
332             * </p>
333             * 
334             * @param inputStream
335             *            The input stream from which encoded data are read.
336             * @param outputStream
337             *            The output stream in which decoded data are written.
338             * @throws IOException
339             *             If an I/O error occurs.
340             */
341            public static void decode(InputStream inputStream, OutputStream outputStream)
342                            throws IOException {
343                    copy(new Base64InputStream(inputStream), outputStream);
344            }
345    
346            /**
347             * Copies data from a stream to another.
348             * 
349             * @param inputStream
350             *            The input stream.
351             * @param outputStream
352             *            The output stream.
353             * @throws IOException
354             *             If a unexpected I/O error occurs.
355             */
356            private static void copy(InputStream inputStream, OutputStream outputStream)
357                            throws IOException {
358                    // 1KB buffer
359                    byte[] b = new byte[1024];
360                    int len;
361                    while ((len = inputStream.read(b)) != -1) {
362                            outputStream.write(b, 0, len);
363                    }
364            }
365    
366    }