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.ByteArrayInputStream;
023    import java.io.ByteArrayOutputStream;
024    import java.io.File;
025    import java.io.FileInputStream;
026    import java.io.FileOutputStream;
027    import java.io.IOException;
028    import java.io.InputStream;
029    import java.io.OutputStream;
030    import java.io.UnsupportedEncodingException;
031    
032    /**
033     * <p>
034     * Base64 encoding and decoding utility methods, both for binary and textual
035     * informations.
036     * </p>
037     * 
038     * @author Carlo Pelliccia
039     * @since 1.1
040     * @version 1.3
041     */
042    public class Base64 {
043    
044            /**
045             * <p>
046             * Encodes a string.
047             * </p>
048             * <p>
049             * Before the string is encoded in Base64, it is converted in a binary
050             * sequence using the system default charset.
051             * </p>
052             * 
053             * @param str
054             *            The source string.
055             * @return The encoded string.
056             * @throws RuntimeException
057             *             If an unexpected error occurs.
058             */
059            public static String encode(String str) throws RuntimeException {
060                    byte[] bytes = str.getBytes();
061                    byte[] encoded = encode(bytes);
062                    try {
063                            return new String(encoded, "ASCII");
064                    } catch (UnsupportedEncodingException e) {
065                            throw new RuntimeException("ASCII is not supported!", e);
066                    }
067            }
068    
069            /**
070             * <p>
071             * Encodes a string.
072             * </p>
073             * <p>
074             * Before the string is encoded in Base64, it is converted in a binary
075             * sequence using the supplied charset.
076             * </p>
077             * 
078             * @param str
079             *            The source string
080             * @param charset
081             *            The charset name.
082             * @return The encoded string.
083             * @throws RuntimeException
084             *             If an unexpected error occurs.
085             * @since 1.2
086             */
087            public static String encode(String str, String charset)
088                            throws RuntimeException {
089                    byte[] bytes;
090                    try {
091                            bytes = str.getBytes(charset);
092                    } catch (UnsupportedEncodingException e) {
093                            throw new RuntimeException("Unsupported charset: " + charset, e);
094                    }
095                    byte[] encoded = encode(bytes);
096                    try {
097                            return new String(encoded, "ASCII");
098                    } catch (UnsupportedEncodingException e) {
099                            throw new RuntimeException("ASCII is not supported!", e);
100                    }
101            }
102    
103            /**
104             * <p>
105             * Decodes the supplied string.
106             * </p>
107             * <p>
108             * The supplied string is decoded into a binary sequence, and then the
109             * sequence is encoded with the system default charset and returned.
110             * </p>
111             * 
112             * @param str
113             *            The encoded string.
114             * @return The decoded string.
115             * @throws RuntimeException
116             *             If an unexpected error occurs.
117             */
118            public static String decode(String str) throws RuntimeException {
119                    byte[] bytes;
120                    try {
121                            bytes = str.getBytes("ASCII");
122                    } catch (UnsupportedEncodingException e) {
123                            throw new RuntimeException("ASCII is not supported!", e);
124                    }
125                    byte[] decoded = decode(bytes);
126                    return new String(decoded);
127            }
128    
129            /**
130             * <p>
131             * Decodes the supplied string.
132             * </p>
133             * <p>
134             * The supplied string is decoded into a binary sequence, and then the
135             * sequence is encoded with the supplied charset and returned.
136             * </p>
137             * 
138             * @param str
139             *            The encoded string.
140             * @param charset
141             *            The charset name.
142             * @return The decoded string.
143             * @throws RuntimeException
144             *             If an unexpected error occurs.
145             * @since 1.2
146             */
147            public static String decode(String str, String charset)
148                            throws RuntimeException {
149                    byte[] bytes;
150                    try {
151                            bytes = str.getBytes("ASCII");
152                    } catch (UnsupportedEncodingException e) {
153                            throw new RuntimeException("ASCII is not supported!", e);
154                    }
155                    byte[] decoded = decode(bytes);
156                    try {
157                            return new String(decoded, charset);
158                    } catch (UnsupportedEncodingException e) {
159                            throw new RuntimeException("Unsupported charset: " + charset, e);
160                    }
161            }
162    
163            /**
164             * <p>
165             * Encodes a binary sequence.
166             * </p>
167             * <p>
168             * If data are large, i.e. if you are working with large binary files,
169             * consider to use a {@link Base64OutputStream} instead of loading too much
170             * data in memory.
171             * </p>
172             * 
173             * @param bytes
174             *            The source sequence.
175             * @return The encoded sequence.
176             * @throws RuntimeException
177             *             If an unexpected error occurs.
178             * @since 1.2
179             */
180            public static byte[] encode(byte[] bytes) throws RuntimeException {
181                    return encode(bytes, 0);
182            }
183    
184            /**
185             * <p>
186             * Encodes a binary sequence, wrapping every encoded line every
187             * <em>wrapAt</em> characters. A <em>wrapAt</em> value less than 1 disables
188             * wrapping.
189             * </p>
190             * <p>
191             * If data are large, i.e. if you are working with large binary files,
192             * consider to use a {@link Base64OutputStream} instead of loading too much
193             * data in memory.
194             * </p>
195             * 
196             * @param bytes
197             *            The source sequence.
198             * @param wrapAt
199             *            The max line length for encoded data. If less than 1 no wrap
200             *            is applied.
201             * @return The encoded sequence.
202             * @throws RuntimeException
203             *             If an unexpected error occurs.
204             * @since 1.2
205             */
206            public static byte[] encode(byte[] bytes, int wrapAt)
207                            throws RuntimeException {
208                    ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
209                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
210                    try {
211                            encode(inputStream, outputStream, wrapAt);
212                    } catch (IOException e) {
213                            throw new RuntimeException("Unexpected I/O error", e);
214                    } finally {
215                            try {
216                                    inputStream.close();
217                            } catch (Throwable t) {
218                                    ;
219                            }
220                            try {
221                                    outputStream.close();
222                            } catch (Throwable t) {
223                                    ;
224                            }
225                    }
226                    return outputStream.toByteArray();
227            }
228    
229            /**
230             * <p>
231             * Decodes a binary sequence.
232             * </p>
233             * <p>
234             * If data are large, i.e. if you are working with large binary files,
235             * consider to use a {@link Base64InputStream} instead of loading too much
236             * data in memory.
237             * </p>
238             * 
239             * @param bytes
240             *            The encoded sequence.
241             * @return The decoded sequence.
242             * @throws RuntimeException
243             *             If an unexpected error occurs.
244             * @since 1.2
245             */
246            public static byte[] decode(byte[] bytes) throws RuntimeException {
247                    ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
248                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
249                    try {
250                            decode(inputStream, outputStream);
251                    } catch (IOException e) {
252                            throw new RuntimeException("Unexpected I/O error", e);
253                    } finally {
254                            try {
255                                    inputStream.close();
256                            } catch (Throwable t) {
257                                    ;
258                            }
259                            try {
260                                    outputStream.close();
261                            } catch (Throwable t) {
262                                    ;
263                            }
264                    }
265                    return outputStream.toByteArray();
266            }
267    
268            /**
269             * <p>
270             * Encodes data from the given input stream and writes them in the given
271             * output stream.
272             * </p>
273             * <p>
274             * The supplied input stream is read until its end is reached, but it's not
275             * closed by this method.
276             * </p>
277             * <p>
278             * The supplied output stream is nor flushed neither closed by this method.
279             * </p>
280             * 
281             * @param inputStream
282             *            The input stream.
283             * @param outputStream
284             *            The output stream.
285             * @throws IOException
286             *             If an I/O error occurs.
287             */
288            public static void encode(InputStream inputStream, OutputStream outputStream)
289                            throws IOException {
290                    encode(inputStream, outputStream, 0);
291            }
292    
293            /**
294             * <p>
295             * Encodes data from the given input stream and writes them in the given
296             * output stream, wrapping every encoded line every <em>wrapAt</em>
297             * characters. A <em>wrapAt</em> value less than 1 disables wrapping.
298             * </p>
299             * <p>
300             * The supplied input stream is read until its end is reached, but it's not
301             * closed by this method.
302             * </p>
303             * <p>
304             * The supplied output stream is nor flushed neither closed by this method.
305             * </p>
306             * 
307             * @param inputStream
308             *            The input stream from which clear data are read.
309             * @param outputStream
310             *            The output stream in which encoded data are written.
311             * @param wrapAt
312             *            The max line length for encoded data. If less than 1 no wrap
313             *            is applied.
314             * @throws IOException
315             *             If an I/O error occurs.
316             */
317            public static void encode(InputStream inputStream,
318                            OutputStream outputStream, int wrapAt) throws IOException {
319                    Base64OutputStream aux = new Base64OutputStream(outputStream, wrapAt);
320                    copy(inputStream, aux);
321                    aux.commit();
322            }
323    
324            /**
325             * <p>
326             * Decodes data from the given input stream and writes them in the given
327             * output stream.
328             * </p>
329             * <p>
330             * The supplied input stream is read until its end is reached, but it's not
331             * closed by this method.
332             * </p>
333             * <p>
334             * The supplied output stream is nor flushed neither closed by this method.
335             * </p>
336             * 
337             * @param inputStream
338             *            The input stream from which encoded data are read.
339             * @param outputStream
340             *            The output stream in which decoded data are written.
341             * @throws IOException
342             *             If an I/O error occurs.
343             */
344            public static void decode(InputStream inputStream, OutputStream outputStream)
345                            throws IOException {
346                    copy(new Base64InputStream(inputStream), outputStream);
347            }
348    
349            /**
350             * <p>
351             * Encodes data from the given source file contents and writes them in the
352             * given target file, wrapping every encoded line every <em>wrapAt</em>
353             * characters. A <em>wrapAt</em> value less than 1 disables wrapping.
354             * </p>
355             * 
356             * @param source
357             *            The source file, from which decoded data are read.
358             * @param target
359             *            The target file, in which encoded data are written.
360             * @param wrapAt
361             *            The max line length for encoded data. If less than 1 no wrap
362             *            is applied.
363             * @throws IOException
364             *             If an I/O error occurs.
365             * @since 1.3
366             */
367            public static void encode(File source, File target, int wrapAt)
368                            throws IOException {
369                    InputStream inputStream = null;
370                    OutputStream outputStream = null;
371                    try {
372                            inputStream = new FileInputStream(source);
373                            outputStream = new FileOutputStream(target);
374                            Base64.encode(inputStream, outputStream, wrapAt);
375                    } finally {
376                            if (outputStream != null) {
377                                    try {
378                                            outputStream.close();
379                                    } catch (Throwable t) {
380                                            ;
381                                    }
382                            }
383                            if (inputStream != null) {
384                                    try {
385                                            inputStream.close();
386                                    } catch (Throwable t) {
387                                            ;
388                                    }
389                            }
390                    }
391            }
392    
393            /**
394             * <p>
395             * Encodes data from the given source file contents and writes them in the
396             * given target file.
397             * </p>
398             * 
399             * @param source
400             *            The source file, from which decoded data are read.
401             * @param target
402             *            The target file, in which encoded data are written.
403             * @throws IOException
404             *             If an I/O error occurs.
405             * @since 1.3
406             */
407            public static void encode(File source, File target) throws IOException {
408                    InputStream inputStream = null;
409                    OutputStream outputStream = null;
410                    try {
411                            inputStream = new FileInputStream(source);
412                            outputStream = new FileOutputStream(target);
413                            Base64.encode(inputStream, outputStream);
414                    } finally {
415                            if (outputStream != null) {
416                                    try {
417                                            outputStream.close();
418                                    } catch (Throwable t) {
419                                            ;
420                                    }
421                            }
422                            if (inputStream != null) {
423                                    try {
424                                            inputStream.close();
425                                    } catch (Throwable t) {
426                                            ;
427                                    }
428                            }
429                    }
430            }
431    
432            /**
433             * <p>
434             * Decodes data from the given source file contents and writes them in the
435             * given target file.
436             * </p>
437             * 
438             * @param source
439             *            The source file, from which encoded data are read.
440             * @param target
441             *            The target file, in which decoded data are written.
442             * @throws IOException
443             *             If an I/O error occurs.
444             * @since 1.3
445             */
446            public static void decode(File source, File target) throws IOException {
447                    InputStream inputStream = null;
448                    OutputStream outputStream = null;
449                    try {
450                            inputStream = new FileInputStream(source);
451                            outputStream = new FileOutputStream(target);
452                            decode(inputStream, outputStream);
453                    } finally {
454                            if (outputStream != null) {
455                                    try {
456                                            outputStream.close();
457                                    } catch (Throwable t) {
458                                            ;
459                                    }
460                            }
461                            if (inputStream != null) {
462                                    try {
463                                            inputStream.close();
464                                    } catch (Throwable t) {
465                                            ;
466                                    }
467                            }
468                    }
469            }
470    
471            /**
472             * Copies data from a stream to another.
473             * 
474             * @param inputStream
475             *            The input stream.
476             * @param outputStream
477             *            The output stream.
478             * @throws IOException
479             *             If a unexpected I/O error occurs.
480             */
481            private static void copy(InputStream inputStream, OutputStream outputStream)
482                            throws IOException {
483                    // 1KB buffer
484                    byte[] b = new byte[1024];
485                    int len;
486                    while ((len = inputStream.read(b)) != -1) {
487                            outputStream.write(b, 0, len);
488                    }
489            }
490    
491    }