]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/util/Base64.java
Re-implement URIStringUtils escape and unescape using Unicode
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / util / Base64.java
1 /*******************************************************************************
2  * This program and the accompanying materials are made available under the
3  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
4  * and is available at http://www.eclipse.org/legal/epl-v10.html
5  *
6  * Contributors:
7  *     Robert Harder <rob@iharder.net> - API and implementation
8  *     VTT Technical Research Centre of Finland - tagged with EPL license
9  *******************************************************************************/
10 package org.simantics.databoard.util;
11
12 /**
13  * <p>Encodes and decodes to and from Base64 notation.</p>
14  * <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p>
15  * 
16  * <p>Example:</p>
17  * 
18  * <code>String encoded = Base64.encode( myByteArray );</code>
19  * <br />
20  * <code>byte[] myByteArray = Base64.decode( encoded );</code>
21  *
22  * <p>The <tt>options</tt> parameter, which appears in a few places, is used to pass 
23  * several pieces of information to the encoder. In the "higher level" methods such as 
24  * encodeBytes( bytes, options ) the options parameter can be used to indicate such 
25  * things as first gzipping the bytes before encoding them, not inserting linefeeds,
26  * and encoding using the URL-safe and Ordered dialects.</p>
27  *
28  * <p>Note, according to <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>,
29  * Section 2.1, implementations should not add line feeds unless explicitly told
30  * to do so. I've got Base64 set to this behavior now, although earlier versions
31  * broke lines by default.</p>
32  *
33  * <p>The constants defined in Base64 can be OR-ed together to combine options, so you 
34  * might make a call like this:</p>
35  *
36  * <code>String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES );</code>
37  * <p>to compress the data before encoding it and then making the output have newline characters.</p>
38  * <p>Also...</p>
39  * <code>String encoded = Base64.encodeBytes( crazyString.getBytes() );</code>
40  *
41  *
42  *
43  * <p>
44  * Change Log:
45  * </p>
46  * <ul>
47  *  <li>v2.3.6 - Fixed bug when breaking lines and the final byte of the encoded
48  *   string ended in the last column; the buffer was not properly shrunk and
49  *   contained an extra (null) byte that made it into the string.</li>
50  *  <li>v2.3.5 - Fixed bug in {@link #encodeFromFile} where estimated buffer size
51  *   was wrong for files of size 31, 34, and 37 bytes.</li>
52  *  <li>v2.3.4 - Fixed bug when working with gzipped streams whereby flushing
53  *   the Base64.OutputStream closed the Base64 encoding (by padding with equals
54  *   signs) too soon. Also added an option to suppress the automatic decoding
55  *   of gzipped streams. Also added experimental support for specifying a
56  *   class loader when using the
57  *   {@link #decodeToObject(java.lang.String, int, java.lang.ClassLoader)}
58  *   method.</li>
59  *  <li>v2.3.3 - Changed default char encoding to US-ASCII which reduces the internal Java
60  *   footprint with its CharEncoders and so forth. Fixed some javadocs that were
61  *   inconsistent. Removed imports and specified things like java.io.IOException
62  *   explicitly inline.</li>
63  *  <li>v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how big the
64  *   final encoded data will be so that the code doesn't have to create two output
65  *   arrays: an oversized initial one and then a final, exact-sized one. Big win
66  *   when using the {@link #encodeBytesToBytes(byte[])} family of methods (and not
67  *   using the gzip options which uses a different mechanism with streams and stuff).</li>
68  *  <li>v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and some
69  *   similar helper methods to be more efficient with memory by not returning a
70  *   String but just a byte array.</li>
71  *  <li>v2.3 - <strong>This is not a drop-in replacement!</strong> This is two years of comments
72  *   and bug fixes queued up and finally executed. Thanks to everyone who sent
73  *   me stuff, and I'm sorry I wasn't able to distribute your fixes to everyone else.
74  *   Much bad coding was cleaned up including throwing exceptions where necessary 
75  *   instead of returning null values or something similar. Here are some changes
76  *   that may affect you:
77  *   <ul>
78  *    <li><em>Does not break lines, by default.</em> This is to keep in compliance with
79  *      <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>.</li>
80  *    <li><em>Throws exceptions instead of returning null values.</em> Because some operations
81  *      (especially those that may permit the GZIP option) use IO streams, there
82  *      is a possiblity of an java.io.IOException being thrown. After some discussion and
83  *      thought, I've changed the behavior of the methods to throw java.io.IOExceptions
84  *      rather than return null if ever there's an error. I think this is more
85  *      appropriate, though it will require some changes to your code. Sorry,
86  *      it should have been done this way to begin with.</li>
87  *    <li><em>Removed all references to System.out, System.err, and the like.</em>
88  *      Shame on me. All I can say is sorry they were ever there.</li>
89  *    <li><em>Throws NullPointerExceptions and IllegalArgumentExceptions</em> as needed
90  *      such as when passed arrays are null or offsets are invalid.</li>
91  *    <li>Cleaned up as much javadoc as I could to avoid any javadoc warnings.
92  *      This was especially annoying before for people who were thorough in their
93  *      own projects and then had gobs of javadoc warnings on this file.</li>
94  *   </ul>
95  *  <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug
96  *   when using very small files (~&lt; 40 bytes).</li>
97  *  <li>v2.2 - Added some helper methods for encoding/decoding directly from
98  *   one file to the next. Also added a main() method to support command line
99  *   encoding/decoding from one file to the next. Also added these Base64 dialects:
100  *   <ol>
101  *   <li>The default is RFC3548 format.</li>
102  *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates
103  *   URL and file name friendly format as described in Section 4 of RFC3548.
104  *   http://www.faqs.org/rfcs/rfc3548.html</li>
105  *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates
106  *   URL and file name friendly format that preserves lexical ordering as described
107  *   in http://www.faqs.org/qa/rfcc-1940.html</li>
108  *   </ol>
109  *   Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a>
110  *   for contributing the new Base64 dialects.
111  *  </li>
112  * 
113  *  <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
114  *   some convenience methods for reading and writing to and from files.</li>
115  *  <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
116  *   with other encodings (like EBCDIC).</li>
117  *  <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
118  *   encoded data was a single byte.</li>
119  *  <li>v2.0 - I got rid of methods that used booleans to set options. 
120  *   Now everything is more consolidated and cleaner. The code now detects
121  *   when data that's being decoded is gzip-compressed and will decompress it
122  *   automatically. Generally things are cleaner. You'll probably have to
123  *   change some method calls that you were making to support the new
124  *   options format (<tt>int</tt>s that you "OR" together).</li>
125  *  <li>v1.5.1 - Fixed bug when decompressing and decoding to a             
126  *   byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.      
127  *   Added the ability to "suspend" encoding in the Output Stream so        
128  *   you can turn on and off the encoding if you need to embed base64       
129  *   data in an otherwise "normal" stream (like an XML file).</li>  
130  *  <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
131  *      This helps when using GZIP streams.
132  *      Added the ability to GZip-compress objects before encoding them.</li>
133  *  <li>v1.4 - Added helper methods to read/write files.</li>
134  *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
135  *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
136  *      where last buffer being read, if not completely full, was not returned.</li>
137  *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
138  *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
139  * </ul>
140  *
141  * <p>
142  * I am placing this code in the Public Domain. Do with it as you will.
143  * This software comes with no guarantees or warranties but with
144  * plenty of well-wishing instead!
145  * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
146  * periodically to check for updates or to contribute improvements.
147  * </p>
148  *
149  * @author Robert Harder
150  * @author rob@iharder.net
151  * @version 2.3.6
152  */
153 public class Base64
154 {
155     
156 /* ********  P U B L I C   F I E L D S  ******** */   
157     
158     
159     /** No options specified. Value is zero. */
160     public final static int NO_OPTIONS = 0;
161     
162     /** Specify encoding in first bit. Value is one. */
163     public final static int ENCODE = 1;
164     
165     
166     /** Specify decoding in first bit. Value is zero. */
167     public final static int DECODE = 0;
168     
169
170     /** Specify that data should be gzip-compressed in second bit. Value is two. */
171     public final static int GZIP = 2;
172
173     /** Specify that gzipped data should <em>not</em> be automatically gunzipped. */
174     public final static int DONT_GUNZIP = 4;
175     
176     
177     /** Do break lines when encoding. Value is 8. */
178     public final static int DO_BREAK_LINES = 8;
179         
180     /** 
181      * Encode using Base64-like encoding that is URL- and Filename-safe as described
182      * in Section 4 of RFC3548: 
183      * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
184      * It is important to note that data encoded this way is <em>not</em> officially valid Base64, 
185      * or at the very least should not be called Base64 without also specifying that is
186      * was encoded using the URL- and Filename-safe dialect.
187      */
188      public final static int URL_SAFE = 16;
189
190
191      /**
192       * Encode using the special "ordered" dialect of Base64 described here:
193       * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
194       */
195      public final static int ORDERED = 32;
196     
197     
198 /* ********  P R I V A T E   F I E L D S  ******** */  
199     
200     
201     /** Maximum line length (76) of Base64 output. */
202     private final static int MAX_LINE_LENGTH = 76;
203     
204     
205     /** The equals sign (=) as a byte. */
206     private final static byte EQUALS_SIGN = (byte)'=';
207     
208     
209     /** The new line character (\n) as a byte. */
210     private final static byte NEW_LINE = (byte)'\n';
211     
212     
213     /** Preferred encoding. */
214     private final static String PREFERRED_ENCODING = "US-ASCII";
215     
216         
217     private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
218     private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
219         
220         
221 /* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */       
222     
223     /** The 64 valid Base64 values. */
224     /* Host platform me be something funny like EBCDIC, so we hardcode these values. */
225     private final static byte[] _STANDARD_ALPHABET = {
226         (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
227         (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
228         (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
229         (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
230         (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
231         (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
232         (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
233         (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
234         (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
235         (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
236     };
237         
238     
239     /** 
240      * Translates a Base64 value to either its 6-bit reconstruction value
241      * or a negative number indicating some other meaning.
242      **/
243     private final static byte[] _STANDARD_DECODABET = {
244         -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
245         -5,-5,                                      // Whitespace: Tab and Linefeed
246         -9,-9,                                      // Decimal 11 - 12
247         -5,                                         // Whitespace: Carriage Return
248         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
249         -9,-9,-9,-9,-9,                             // Decimal 27 - 31
250         -5,                                         // Whitespace: Space
251         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
252         62,                                         // Plus sign at decimal 43
253         -9,-9,-9,                                   // Decimal 44 - 46
254         63,                                         // Slash at decimal 47
255         52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
256         -9,-9,-9,                                   // Decimal 58 - 60
257         -1,                                         // Equals sign at decimal 61
258         -9,-9,-9,                                      // Decimal 62 - 64
259         0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
260         14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
261         -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
262         26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
263         39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
264         -9,-9,-9,-9                                 // Decimal 123 - 126
265         /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
266         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
267         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
268         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
269         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
270         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
271         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
272         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
273         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
274         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
275     };
276         
277         
278 /* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */
279         
280     /**
281      * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: 
282      * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
283      * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
284      */
285     private final static byte[] _URL_SAFE_ALPHABET = {
286       (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
287       (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
288       (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
289       (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
290       (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
291       (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
292       (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
293       (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
294       (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
295       (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
296     };
297         
298     /**
299      * Used in decoding URL- and Filename-safe dialects of Base64.
300      */
301     private final static byte[] _URL_SAFE_DECODABET = {
302       -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
303       -5,-5,                                      // Whitespace: Tab and Linefeed
304       -9,-9,                                      // Decimal 11 - 12
305       -5,                                         // Whitespace: Carriage Return
306       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
307       -9,-9,-9,-9,-9,                             // Decimal 27 - 31
308       -5,                                         // Whitespace: Space
309       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
310       -9,                                         // Plus sign at decimal 43
311       -9,                                         // Decimal 44
312       62,                                         // Minus sign at decimal 45
313       -9,                                         // Decimal 46
314       -9,                                         // Slash at decimal 47
315       52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
316       -9,-9,-9,                                   // Decimal 58 - 60
317       -1,                                         // Equals sign at decimal 61
318       -9,-9,-9,                                   // Decimal 62 - 64
319       0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
320       14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
321       -9,-9,-9,-9,                                // Decimal 91 - 94
322       63,                                         // Underscore at decimal 95
323       -9,                                         // Decimal 96
324       26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
325       39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
326       -9,-9,-9,-9                                 // Decimal 123 - 126
327       /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
328       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
329       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
330       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
331       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
332       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
333       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
334       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
335       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
336       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
337     };
338
339
340
341 /* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */
342
343     /**
344      * I don't get the point of this technique, but someone requested it,
345      * and it is described here:
346      * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
347      */
348     private final static byte[] _ORDERED_ALPHABET = {
349       (byte)'-',
350       (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
351       (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
352       (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
353       (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
354       (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
355       (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
356       (byte)'_',
357       (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
358       (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
359       (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
360       (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
361     };
362         
363     /**
364      * Used in decoding the "ordered" dialect of Base64.
365      */
366     private final static byte[] _ORDERED_DECODABET = {
367       -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
368       -5,-5,                                      // Whitespace: Tab and Linefeed
369       -9,-9,                                      // Decimal 11 - 12
370       -5,                                         // Whitespace: Carriage Return
371       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
372       -9,-9,-9,-9,-9,                             // Decimal 27 - 31
373       -5,                                         // Whitespace: Space
374       -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
375       -9,                                         // Plus sign at decimal 43
376       -9,                                         // Decimal 44
377       0,                                          // Minus sign at decimal 45
378       -9,                                         // Decimal 46
379       -9,                                         // Slash at decimal 47
380       1,2,3,4,5,6,7,8,9,10,                       // Numbers zero through nine
381       -9,-9,-9,                                   // Decimal 58 - 60
382       -1,                                         // Equals sign at decimal 61
383       -9,-9,-9,                                   // Decimal 62 - 64
384       11,12,13,14,15,16,17,18,19,20,21,22,23,     // Letters 'A' through 'M'
385       24,25,26,27,28,29,30,31,32,33,34,35,36,     // Letters 'N' through 'Z'
386       -9,-9,-9,-9,                                // Decimal 91 - 94
387       37,                                         // Underscore at decimal 95
388       -9,                                         // Decimal 96
389       38,39,40,41,42,43,44,45,46,47,48,49,50,     // Letters 'a' through 'm'
390       51,52,53,54,55,56,57,58,59,60,61,62,63,     // Letters 'n' through 'z'
391       -9,-9,-9,-9                                 // Decimal 123 - 126
392       /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
393         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
394         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
395         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
396         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
397         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
398         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
399         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
400         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
401         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
402     };
403
404         
405 /* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */
406
407
408     /**
409      * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
410      * the options specified.
411      * It's possible, though silly, to specify ORDERED <b>and</b> URLSAFE
412      * in which case one of them will be picked, though there is
413      * no guarantee as to which one will be picked.
414      */
415     private final static byte[] getAlphabet( int options ) {
416         if ((options & URL_SAFE) == URL_SAFE) {
417             return _URL_SAFE_ALPHABET;
418         } else if ((options & ORDERED) == ORDERED) {
419             return _ORDERED_ALPHABET;
420         } else {
421             return _STANDARD_ALPHABET;
422         }
423     }   // end getAlphabet
424
425
426     /**
427      * Returns one of the _SOMETHING_DECODABET byte arrays depending on
428      * the options specified.
429      * It's possible, though silly, to specify ORDERED and URL_SAFE
430      * in which case one of them will be picked, though there is
431      * no guarantee as to which one will be picked.
432      */
433     private final static byte[] getDecodabet( int options ) {
434         if( (options & URL_SAFE) == URL_SAFE) {
435             return _URL_SAFE_DECODABET;
436         } else if ((options & ORDERED) == ORDERED) {
437             return _ORDERED_DECODABET;
438         } else {
439             return _STANDARD_DECODABET;
440         }
441     }   // end getAlphabet
442
443
444     
445     /** Defeats instantiation. */
446     private Base64(){}
447     
448
449     
450     
451 /* ********  E N C O D I N G   M E T H O D S  ******** */    
452     
453     
454     /**
455      * Encodes up to the first three bytes of array <var>threeBytes</var>
456      * and returns a four-byte array in Base64 notation.
457      * The actual number of significant bytes in your array is
458      * given by <var>numSigBytes</var>.
459      * The array <var>threeBytes</var> needs only be as big as
460      * <var>numSigBytes</var>.
461      * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
462      *
463      * @param b4 A reusable byte array to reduce array instantiation
464      * @param threeBytes the array to convert
465      * @param numSigBytes the number of significant bytes in your array
466      * @return four byte array in Base64 notation.
467      * @since 1.5.1
468      */
469     private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) {
470         encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
471         return b4;
472     }   // end encode3to4
473
474     
475     /**
476      * <p>Encodes up to three bytes of the array <var>source</var>
477      * and writes the resulting four Base64 bytes to <var>destination</var>.
478      * The source and destination arrays can be manipulated
479      * anywhere along their length by specifying 
480      * <var>srcOffset</var> and <var>destOffset</var>.
481      * This method does not check to make sure your arrays
482      * are large enough to accomodate <var>srcOffset</var> + 3 for
483      * the <var>source</var> array or <var>destOffset</var> + 4 for
484      * the <var>destination</var> array.
485      * The actual number of significant bytes in your array is
486      * given by <var>numSigBytes</var>.</p>
487          * <p>This is the lowest level of the encoding methods with
488          * all possible parameters.</p>
489      *
490      * @param source the array to convert
491      * @param srcOffset the index where conversion begins
492      * @param numSigBytes the number of significant bytes in your array
493      * @param destination the array to hold the conversion
494      * @param destOffset the index where output will be put
495      * @return the <var>destination</var> array
496      * @since 1.3
497      */
498     private static byte[] encode3to4( 
499     byte[] source, int srcOffset, int numSigBytes,
500     byte[] destination, int destOffset, int options ) {
501         
502         byte[] ALPHABET = getAlphabet( options ); 
503         
504         //           1         2         3  
505         // 01234567890123456789012345678901 Bit position
506         // --------000000001111111122222222 Array position from threeBytes
507         // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
508         //          >>18  >>12  >> 6  >> 0  Right shift necessary
509         //                0x3f  0x3f  0x3f  Additional AND
510         
511         // Create buffer with zero-padding if there are only one or two
512         // significant bytes passed in the array.
513         // We have to shift left 24 in order to flush out the 1's that appear
514         // when Java treats a value as negative that is cast from a byte to an int.
515         int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
516                      | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
517                      | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
518
519         switch( numSigBytes )
520         {
521             case 3:
522                 destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
523                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
524                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
525                 destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
526                 return destination;
527                 
528             case 2:
529                 destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
530                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
531                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
532                 destination[ destOffset + 3 ] = EQUALS_SIGN;
533                 return destination;
534                 
535             case 1:
536                 destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
537                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
538                 destination[ destOffset + 2 ] = EQUALS_SIGN;
539                 destination[ destOffset + 3 ] = EQUALS_SIGN;
540                 return destination;
541                 
542             default:
543                 return destination;
544         }   // end switch
545     }   // end encode3to4
546
547
548
549     /**
550      * Performs Base64 encoding on the <code>raw</code> ByteBuffer,
551      * writing it to the <code>encoded</code> ByteBuffer.
552      * This is an experimental feature. Currently it does not
553      * pass along any options (such as {@link #DO_BREAK_LINES}
554      * or {@link #GZIP}.
555      *
556      * @param raw input buffer
557      * @param encoded output buffer
558      * @since 2.3
559      */
560     public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){
561         byte[] raw3 = new byte[3];
562         byte[] enc4 = new byte[4];
563
564         while( raw.hasRemaining() ){
565             int rem = Math.min(3,raw.remaining());
566             raw.get(raw3,0,rem);
567             Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
568             encoded.put(enc4);
569         }   // end input remaining
570     }
571
572
573     /**
574      * Performs Base64 encoding on the <code>raw</code> ByteBuffer,
575      * writing it to the <code>encoded</code> CharBuffer.
576      * This is an experimental feature. Currently it does not
577      * pass along any options (such as {@link #DO_BREAK_LINES}
578      * or {@link #GZIP}.
579      *
580      * @param raw input buffer
581      * @param encoded output buffer
582      * @since 2.3
583      */
584     public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){
585         byte[] raw3 = new byte[3];
586         byte[] enc4 = new byte[4];
587
588         while( raw.hasRemaining() ){
589             int rem = Math.min(3,raw.remaining());
590             raw.get(raw3,0,rem);
591             Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
592             for( int i = 0; i < 4; i++ ){
593                 encoded.put( (char)(enc4[i] & 0xFF) );
594             }
595         }   // end input remaining
596     }
597
598
599     
600     
601     /**
602      * Serializes an object and returns the Base64-encoded
603      * version of that serialized object.  
604      *  
605      * <p>As of v 2.3, if the object
606      * cannot be serialized or there is another error,
607      * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
608      * In earlier versions, it just returned a null value, but
609      * in retrospect that's a pretty poor way to handle it.</p>
610      * 
611      * The object is not GZip-compressed before being encoded.
612      *
613      * @param serializableObject The object to encode
614      * @return The Base64-encoded object
615      * @throws java.io.IOException if there is an error
616      * @throws NullPointerException if serializedObject is null
617      * @since 1.4
618      */
619     public static String encodeObject( java.io.Serializable serializableObject )
620     throws java.io.IOException {
621         return encodeObject( serializableObject, NO_OPTIONS );
622     }   // end encodeObject
623     
624
625
626     /**
627      * Serializes an object and returns the Base64-encoded
628      * version of that serialized object.
629      *  
630      * <p>As of v 2.3, if the object
631      * cannot be serialized or there is another error,
632      * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
633      * In earlier versions, it just returned a null value, but
634      * in retrospect that's a pretty poor way to handle it.</p>
635      * 
636      * The object is not GZip-compressed before being encoded.
637      * <p>
638      * Example options:<pre>
639      *   GZIP: gzip-compresses object before encoding it.
640      *   DO_BREAK_LINES: break lines at 76 characters
641      * </pre>
642      * <p>
643      * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
644      * <p>
645      * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
646      *
647      * @param serializableObject The object to encode
648      * @param options Specified options
649      * @return The Base64-encoded object
650      * @see Base64#GZIP
651      * @see Base64#DO_BREAK_LINES
652      * @throws java.io.IOException if there is an error
653      * @since 2.0
654      */
655     public static String encodeObject( java.io.Serializable serializableObject, int options )
656     throws java.io.IOException {
657
658         if( serializableObject == null ){
659             throw new NullPointerException( "Cannot serialize a null object." );
660         }   // end if: null
661         
662         // Streams
663         java.io.ByteArrayOutputStream  baos  = null; 
664         java.io.OutputStream           b64os = null;
665         java.util.zip.GZIPOutputStream gzos  = null;
666         java.io.ObjectOutputStream     oos   = null;
667         
668         
669         try {
670             // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
671             baos  = new java.io.ByteArrayOutputStream();
672             b64os = new Base64.OutputStream( baos, ENCODE | options );
673             if( (options & GZIP) != 0 ){
674                 // Gzip
675                 gzos = new java.util.zip.GZIPOutputStream(b64os);
676                 oos = new java.io.ObjectOutputStream( gzos );
677             } else {
678                 // Not gzipped
679                 oos = new java.io.ObjectOutputStream( b64os );
680             }
681             oos.writeObject( serializableObject );
682         }   // end try
683         catch( java.io.IOException e ) {
684             // Catch it and then throw it immediately so that
685             // the finally{} block is called for cleanup.
686             throw e;
687         }   // end catch
688         finally {
689             try{ oos.close();   } catch( Exception e ){}
690             try{ gzos.close();  } catch( Exception e ){}
691             try{ b64os.close(); } catch( Exception e ){}
692             try{ baos.close();  } catch( Exception e ){}
693         }   // end finally
694         
695         // Return value according to relevant encoding.
696         try {
697             return new String( baos.toByteArray(), PREFERRED_ENCODING );
698         }   // end try
699         catch (java.io.UnsupportedEncodingException uue){
700             // Fall back to some Java default
701             return new String( baos.toByteArray() );
702         }   // end catch
703         
704     }   // end encode
705     
706     
707     /**
708      * Encodes a byte array into Base64 notation.
709      * Does not GZip-compress data.
710      * 
711      * Added by Tuukka Lehtonen for backwards binary compatibility
712      * with previous Base64 Simantics implementation.
713      * 
714      * @param source The data to convert
715      * @return The data in Base64-encoded form
716      * @throws NullPointerException if source array is null
717      */
718     public static String encode( byte[] source ) {
719         return encodeBytes( source );
720     }   // end encode
721
722     /**
723      * Encodes a byte array into Base64 notation.
724      * Does not GZip-compress data.
725      *  
726      * @param source The data to convert
727      * @return The data in Base64-encoded form
728      * @throws NullPointerException if source array is null
729      * @since 1.4
730      */
731     public static String encodeBytes( byte[] source ) {
732         // Since we're not going to have the GZIP encoding turned on,
733         // we're not going to have an java.io.IOException thrown, so
734         // we should not force the user to have to catch it.
735         String encoded = null;
736         try {
737             encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
738         } catch (java.io.IOException ex) {
739             assert false : ex.getMessage();
740         }   // end catch
741         assert encoded != null;
742         return encoded;
743     }   // end encodeBytes
744     
745
746
747     /**
748      * Encodes a byte array into Base64 notation.
749      * <p>
750      * Example options:<pre>
751      *   GZIP: gzip-compresses object before encoding it.
752      *   DO_BREAK_LINES: break lines at 76 characters
753      *     <i>Note: Technically, this makes your encoding non-compliant.</i>
754      * </pre>
755      * <p>
756      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
757      * <p>
758      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
759      *
760      *  
761      * <p>As of v 2.3, if there is an error with the GZIP stream,
762      * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
763      * In earlier versions, it just returned a null value, but
764      * in retrospect that's a pretty poor way to handle it.</p>
765      * 
766      *
767      * @param source The data to convert
768      * @param options Specified options
769      * @return The Base64-encoded data as a String
770      * @see Base64#GZIP
771      * @see Base64#DO_BREAK_LINES
772      * @throws java.io.IOException if there is an error
773      * @throws NullPointerException if source array is null
774      * @since 2.0
775      */
776     public static String encodeBytes( byte[] source, int options ) throws java.io.IOException {
777         return encodeBytes( source, 0, source.length, options );
778     }   // end encodeBytes
779     
780     
781     /**
782      * Encodes a byte array into Base64 notation.
783      * Does not GZip-compress data.
784      *  
785      * <p>As of v 2.3, if there is an error,
786      * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
787      * In earlier versions, it just returned a null value, but
788      * in retrospect that's a pretty poor way to handle it.</p>
789      * 
790      *
791      * @param source The data to convert
792      * @param off Offset in array where conversion should begin
793      * @param len Length of data to convert
794      * @return The Base64-encoded data as a String
795      * @throws NullPointerException if source array is null
796      * @throws IllegalArgumentException if source array, offset, or length are invalid
797      * @since 1.4
798      */
799     public static String encodeBytes( byte[] source, int off, int len ) {
800         // Since we're not going to have the GZIP encoding turned on,
801         // we're not going to have an java.io.IOException thrown, so
802         // we should not force the user to have to catch it.
803         String encoded = null;
804         try {
805             encoded = encodeBytes( source, off, len, NO_OPTIONS );
806         } catch (java.io.IOException ex) {
807             assert false : ex.getMessage();
808         }   // end catch
809         assert encoded != null;
810         return encoded;
811     }   // end encodeBytes
812     
813     
814
815     /**
816      * Encodes a byte array into Base64 notation.
817      * <p>
818      * Example options:<pre>
819      *   GZIP: gzip-compresses object before encoding it.
820      *   DO_BREAK_LINES: break lines at 76 characters
821      *     <i>Note: Technically, this makes your encoding non-compliant.</i>
822      * </pre>
823      * <p>
824      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
825      * <p>
826      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
827      *
828      *  
829      * <p>As of v 2.3, if there is an error with the GZIP stream,
830      * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
831      * In earlier versions, it just returned a null value, but
832      * in retrospect that's a pretty poor way to handle it.</p>
833      * 
834      *
835      * @param source The data to convert
836      * @param off Offset in array where conversion should begin
837      * @param len Length of data to convert
838      * @param options Specified options
839      * @return The Base64-encoded data as a String
840      * @see Base64#GZIP
841      * @see Base64#DO_BREAK_LINES
842      * @throws java.io.IOException if there is an error
843      * @throws NullPointerException if source array is null
844      * @throws IllegalArgumentException if source array, offset, or length are invalid
845      * @since 2.0
846      */
847     public static String encodeBytes( byte[] source, int off, int len, int options ) throws java.io.IOException {
848         byte[] encoded = encodeBytesToBytes( source, off, len, options );
849
850         // Return value according to relevant encoding.
851         try {
852             return new String( encoded, PREFERRED_ENCODING );
853         }   // end try
854         catch (java.io.UnsupportedEncodingException uue) {
855             return new String( encoded );
856         }   // end catch
857         
858     }   // end encodeBytes
859
860
861
862
863     /**
864      * Similar to {@link #encodeBytes(byte[])} but returns
865      * a byte array instead of instantiating a String. This is more efficient
866      * if you're working with I/O streams and have large data sets to encode.
867      *
868      *
869      * @param source The data to convert
870      * @return The Base64-encoded data as a byte[] (of ASCII characters)
871      * @throws NullPointerException if source array is null
872      * @since 2.3.1
873      */
874     public static byte[] encodeBytesToBytes( byte[] source ) {
875         byte[] encoded = null;
876         try {
877             encoded = encodeBytesToBytes( source, 0, source.length, Base64.NO_OPTIONS );
878         } catch( java.io.IOException ex ) {
879             assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
880         }
881         return encoded;
882     }
883
884
885     /**
886      * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns
887      * a byte array instead of instantiating a String. This is more efficient
888      * if you're working with I/O streams and have large data sets to encode.
889      *
890      *
891      * @param source The data to convert
892      * @param off Offset in array where conversion should begin
893      * @param len Length of data to convert
894      * @param options Specified options
895      * @return The Base64-encoded data as a String
896      * @see Base64#GZIP
897      * @see Base64#DO_BREAK_LINES
898      * @throws java.io.IOException if there is an error
899      * @throws NullPointerException if source array is null
900      * @throws IllegalArgumentException if source array, offset, or length are invalid
901      * @since 2.3.1
902      */
903     public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException {
904
905         if( source == null ){
906             throw new NullPointerException( "Cannot serialize a null array." );
907         }   // end if: null
908
909         if( off < 0 ){
910             throw new IllegalArgumentException( "Cannot have negative offset: " + off );
911         }   // end if: off < 0
912
913         if( len < 0 ){
914             throw new IllegalArgumentException( "Cannot have length offset: " + len );
915         }   // end if: len < 0
916
917         if( off + len > source.length  ){
918             throw new IllegalArgumentException(
919             String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length));
920         }   // end if: off < 0
921
922
923
924         // Compress?
925         if( (options & GZIP) != 0 ) {
926             java.io.ByteArrayOutputStream  baos  = null;
927             java.util.zip.GZIPOutputStream gzos  = null;
928             Base64.OutputStream            b64os = null;
929
930             try {
931                 // GZip -> Base64 -> ByteArray
932                 baos = new java.io.ByteArrayOutputStream();
933                 b64os = new Base64.OutputStream( baos, ENCODE | options );
934                 gzos  = new java.util.zip.GZIPOutputStream( b64os );
935
936                 gzos.write( source, off, len );
937                 gzos.close();
938             }   // end try
939             catch( java.io.IOException e ) {
940                 // Catch it and then throw it immediately so that
941                 // the finally{} block is called for cleanup.
942                 throw e;
943             }   // end catch
944             finally {
945                 try{ gzos.close();  } catch( Exception e ){}
946                 try{ b64os.close(); } catch( Exception e ){}
947                 try{ baos.close();  } catch( Exception e ){}
948             }   // end finally
949
950             return baos.toByteArray();
951         }   // end if: compress
952
953         // Else, don't compress. Better not to use streams at all then.
954         else {
955             boolean breakLines = (options & DO_BREAK_LINES) != 0;
956
957             //int    len43   = len * 4 / 3;
958             //byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
959             //                           + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
960             //                           + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
961             // Try to determine more precisely how big the array needs to be.
962             // If we get it right, we don't have to do an array copy, and
963             // we save a bunch of memory.
964             int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding
965             if( breakLines ){
966                 encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters
967             }
968             byte[] outBuff = new byte[ encLen ];
969
970
971             int d = 0;
972             int e = 0;
973             int len2 = len - 2;
974             int lineLength = 0;
975             for( ; d < len2; d+=3, e+=4 ) {
976                 encode3to4( source, d+off, 3, outBuff, e, options );
977
978                 lineLength += 4;
979                 if( breakLines && lineLength >= MAX_LINE_LENGTH )
980                 {
981                     outBuff[e+4] = NEW_LINE;
982                     e++;
983                     lineLength = 0;
984                 }   // end if: end of line
985             }   // en dfor: each piece of array
986
987             if( d < len ) {
988                 encode3to4( source, d+off, len - d, outBuff, e, options );
989                 e += 4;
990             }   // end if: some padding needed
991
992
993             // Only resize array if we didn't guess it right.
994             if( e <= outBuff.length - 1 ){
995                 // If breaking lines and the last byte falls right at
996                 // the line length (76 bytes per line), there will be
997                 // one extra byte, and the array will need to be resized.
998                 // Not too bad of an estimate on array size, I'd say.
999                 byte[] finalOut = new byte[e];
1000                 System.arraycopy(outBuff,0, finalOut,0,e);
1001                 //System.err.println("Having to resize array from " + outBuff.length + " to " + e );
1002                 return finalOut;
1003             } else {
1004                 //System.err.println("No need to resize array.");
1005                 return outBuff;
1006             }
1007         
1008         }   // end else: don't compress
1009
1010     }   // end encodeBytesToBytes
1011     
1012
1013     
1014     
1015     
1016 /* ********  D E C O D I N G   M E T H O D S  ******** */
1017     
1018     
1019     /**
1020      * Decodes four bytes from array <var>source</var>
1021      * and writes the resulting bytes (up to three of them)
1022      * to <var>destination</var>.
1023      * The source and destination arrays can be manipulated
1024      * anywhere along their length by specifying 
1025      * <var>srcOffset</var> and <var>destOffset</var>.
1026      * This method does not check to make sure your arrays
1027      * are large enough to accomodate <var>srcOffset</var> + 4 for
1028      * the <var>source</var> array or <var>destOffset</var> + 3 for
1029      * the <var>destination</var> array.
1030      * This method returns the actual number of bytes that 
1031      * were converted from the Base64 encoding.
1032          * <p>This is the lowest level of the decoding methods with
1033          * all possible parameters.</p>
1034      * 
1035      *
1036      * @param source the array to convert
1037      * @param srcOffset the index where conversion begins
1038      * @param destination the array to hold the conversion
1039      * @param destOffset the index where output will be put
1040          * @param options alphabet type is pulled from this (standard, url-safe, ordered)
1041      * @return the number of decoded bytes converted
1042      * @throws NullPointerException if source or destination arrays are null
1043      * @throws IllegalArgumentException if srcOffset or destOffset are invalid
1044      *         or there is not enough room in the array.
1045      * @since 1.3
1046      */
1047     private static int decode4to3( 
1048     byte[] source, int srcOffset, 
1049     byte[] destination, int destOffset, int options ) {
1050         
1051         // Lots of error checking and exception throwing
1052         if( source == null ){
1053             throw new NullPointerException( "Source array was null." );
1054         }   // end if
1055         if( destination == null ){
1056             throw new NullPointerException( "Destination array was null." );
1057         }   // end if
1058         if( srcOffset < 0 || srcOffset + 3 >= source.length ){
1059             throw new IllegalArgumentException( String.format(
1060             "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) );
1061         }   // end if
1062         if( destOffset < 0 || destOffset +2 >= destination.length ){
1063             throw new IllegalArgumentException( String.format(
1064             "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) );
1065         }   // end if
1066         
1067         
1068         byte[] DECODABET = getDecodabet( options ); 
1069         
1070         // Example: Dk==
1071         if( source[ srcOffset + 2] == EQUALS_SIGN ) {
1072             // Two ways to do the same thing. Don't know which way I like best.
1073           //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
1074           //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
1075             int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
1076                           | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
1077             
1078             destination[ destOffset ] = (byte)( outBuff >>> 16 );
1079             return 1;
1080         }
1081         
1082         // Example: DkL=
1083         else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) {
1084             // Two ways to do the same thing. Don't know which way I like best.
1085           //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
1086           //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1087           //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
1088             int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
1089                           | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
1090                           | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
1091             
1092             destination[ destOffset     ] = (byte)( outBuff >>> 16 );
1093             destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
1094             return 2;
1095         }
1096         
1097         // Example: DkLE
1098         else {
1099             // Two ways to do the same thing. Don't know which way I like best.
1100           //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
1101           //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1102           //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
1103           //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
1104             int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
1105                           | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
1106                           | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
1107                           | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
1108
1109             
1110             destination[ destOffset     ] = (byte)( outBuff >> 16 );
1111             destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
1112             destination[ destOffset + 2 ] = (byte)( outBuff       );
1113
1114             return 3;
1115         }
1116     }   // end decodeToBytes
1117     
1118
1119
1120
1121
1122     /**
1123      * Low-level access to decoding ASCII characters in
1124      * the form of a byte array. <strong>Ignores GUNZIP option, if
1125      * it's set.</strong> This is not generally a recommended method,
1126      * although it is used internally as part of the decoding process.
1127      * Special case: if len = 0, an empty array is returned. Still,
1128      * if you need more speed and reduced memory footprint (and aren't
1129      * gzipping), consider this method.
1130      *
1131      * @param source The Base64 encoded data
1132      * @return decoded data
1133      * @since 2.3.1
1134      */
1135     public static byte[] decode( byte[] source ){
1136         byte[] decoded = null;
1137         try {
1138             decoded = decode( source, 0, source.length, Base64.NO_OPTIONS );
1139         } catch( java.io.IOException ex ) {
1140             assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
1141         }
1142         return decoded;
1143     }
1144
1145     
1146     
1147     /**
1148      * Low-level access to decoding ASCII characters in
1149      * the form of a byte array. <strong>Ignores GUNZIP option, if
1150      * it's set.</strong> This is not generally a recommended method,
1151      * although it is used internally as part of the decoding process.
1152      * Special case: if len = 0, an empty array is returned. Still,
1153      * if you need more speed and reduced memory footprint (and aren't
1154      * gzipping), consider this method.
1155      *
1156      * @param source The Base64 encoded data
1157      * @param off    The offset of where to begin decoding
1158      * @param len    The length of characters to decode
1159      * @param options Can specify options such as alphabet type to use
1160      * @return decoded data
1161      * @throws java.io.IOException If bogus characters exist in source data
1162      * @since 1.3
1163      */
1164     public static byte[] decode( byte[] source, int off, int len, int options )
1165     throws java.io.IOException {
1166         
1167         // Lots of error checking and exception throwing
1168         if( source == null ){
1169             throw new NullPointerException( "Cannot decode null source array." );
1170         }   // end if
1171         if( off < 0 || off + len > source.length ){
1172             throw new IllegalArgumentException( String.format(
1173             "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) );
1174         }   // end if
1175         
1176         if( len == 0 ){
1177             return new byte[0];
1178         }else if( len < 4 ){
1179             throw new IllegalArgumentException( 
1180             "Base64-encoded string must have at least four characters, but length specified was " + len );
1181         }   // end if
1182         
1183         byte[] DECODABET = getDecodabet( options );
1184         
1185         int    len34   = len * 3 / 4;       // Estimate on array size
1186         byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
1187         int    outBuffPosn = 0;             // Keep track of where we're writing
1188         
1189         byte[] b4        = new byte[4];     // Four byte buffer from source, eliminating white space
1190         int    b4Posn    = 0;               // Keep track of four byte input buffer
1191         int    i         = 0;               // Source array counter
1192         byte   sbiCrop   = 0;               // Low seven bits (ASCII) of input
1193         byte   sbiDecode = 0;               // Special value from DECODABET
1194         
1195         for( i = off; i < off+len; i++ ) {  // Loop through source
1196             
1197             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
1198             sbiDecode = DECODABET[ sbiCrop ];   // Special value
1199             
1200             // White space, Equals sign, or legit Base64 character
1201             // Note the values such as -5 and -9 in the
1202             // DECODABETs at the top of the file.
1203             if( sbiDecode >= WHITE_SPACE_ENC )  {
1204                 if( sbiDecode >= EQUALS_SIGN_ENC ) {
1205                     b4[ b4Posn++ ] = sbiCrop;           // Save non-whitespace
1206                     if( b4Posn > 3 ) {                  // Time to decode?
1207                         outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
1208                         b4Posn = 0;
1209                         
1210                         // If that was the equals sign, break out of 'for' loop
1211                         if( sbiCrop == EQUALS_SIGN ) {
1212                             break;
1213                         }   // end if: equals sign
1214                     }   // end if: quartet built
1215                 }   // end if: equals sign or better
1216             }   // end if: white space, equals sign or better
1217             else {
1218                 // There's a bad input character in the Base64 stream.
1219                 throw new java.io.IOException( String.format(
1220                 "Bad Base64 input character '%c' in array position %d", source[i], i ) );
1221             }   // end else: 
1222         }   // each input character
1223                                    
1224         byte[] out = new byte[ outBuffPosn ];
1225         System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 
1226         return out;
1227     }   // end decode
1228     
1229     
1230         
1231         
1232     /**
1233      * Decodes data from Base64 notation, automatically
1234      * detecting gzip-compressed data and decompressing it.
1235      *
1236      * @param s the string to decode
1237      * @return the decoded data
1238      * @throws java.io.IOException If there is a problem
1239      * @since 1.4
1240      */
1241     public static byte[] decode( String s ) throws java.io.IOException {
1242         return decode( s, NO_OPTIONS );
1243     }
1244
1245     
1246     
1247     /**
1248      * Decodes data from Base64 notation, automatically
1249      * detecting gzip-compressed data and decompressing it.
1250      *
1251      * @param s the string to decode
1252      * @param options encode options such as URL_SAFE
1253      * @return the decoded data
1254      * @throws java.io.IOException if there is an error
1255      * @throws NullPointerException if <tt>s</tt> is null
1256      * @since 1.4
1257      */
1258     public static byte[] decode( String s, int options ) throws java.io.IOException {
1259         
1260         if( s == null ){
1261             throw new NullPointerException( "Input string was null." );
1262         }   // end if
1263         
1264         byte[] bytes;
1265         try {
1266             bytes = s.getBytes( PREFERRED_ENCODING );
1267         }   // end try
1268         catch( java.io.UnsupportedEncodingException uee ) {
1269             bytes = s.getBytes();
1270         }   // end catch
1271                 //</change>
1272         
1273         // Decode
1274         bytes = decode( bytes, 0, bytes.length, options );
1275         
1276         // Check to see if it's gzip-compressed
1277         // GZIP Magic Two-Byte Number: 0x8b1f (35615)
1278         boolean dontGunzip = (options & DONT_GUNZIP) != 0;
1279         if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) {
1280             
1281             int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
1282             if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head )  {
1283                 java.io.ByteArrayInputStream  bais = null;
1284                 java.util.zip.GZIPInputStream gzis = null;
1285                 java.io.ByteArrayOutputStream baos = null;
1286                 byte[] buffer = new byte[2048];
1287                 int    length = 0;
1288
1289                 try {
1290                     baos = new java.io.ByteArrayOutputStream();
1291                     bais = new java.io.ByteArrayInputStream( bytes );
1292                     gzis = new java.util.zip.GZIPInputStream( bais );
1293
1294                     while( ( length = gzis.read( buffer ) ) >= 0 ) {
1295                         baos.write(buffer,0,length);
1296                     }   // end while: reading input
1297
1298                     // No error? Get new bytes.
1299                     bytes = baos.toByteArray();
1300
1301                 }   // end try
1302                 catch( java.io.IOException e ) {
1303                     e.printStackTrace();
1304                     // Just return originally-decoded bytes
1305                 }   // end catch
1306                 finally {
1307                     try{ baos.close(); } catch( Exception e ){}
1308                     try{ gzis.close(); } catch( Exception e ){}
1309                     try{ bais.close(); } catch( Exception e ){}
1310                 }   // end finally
1311
1312             }   // end if: gzipped
1313         }   // end if: bytes.length >= 2
1314         
1315         return bytes;
1316     }   // end decode
1317
1318
1319
1320     /**
1321      * Attempts to decode Base64 data and deserialize a Java
1322      * Object within. Returns <tt>null</tt> if there was an error.
1323      *
1324      * @param encodedObject The Base64 data to decode
1325      * @return The decoded and deserialized object
1326      * @throws NullPointerException if encodedObject is null
1327      * @throws java.io.IOException if there is a general error
1328      * @throws ClassNotFoundException if the decoded object is of a
1329      *         class that cannot be found by the JVM
1330      * @since 1.5
1331      */
1332     public static Object decodeToObject( String encodedObject )
1333     throws java.io.IOException, java.lang.ClassNotFoundException {
1334         return decodeToObject(encodedObject,NO_OPTIONS,null);
1335     }
1336     
1337
1338     /**
1339      * Attempts to decode Base64 data and deserialize a Java
1340      * Object within. Returns <tt>null</tt> if there was an error.
1341      * If <tt>loader</tt> is not null, it will be the class loader
1342      * used when deserializing.
1343      *
1344      * @param encodedObject The Base64 data to decode
1345      * @param options Various parameters related to decoding
1346      * @param loader Optional class loader to use in deserializing classes.
1347      * @return The decoded and deserialized object
1348      * @throws NullPointerException if encodedObject is null
1349      * @throws java.io.IOException if there is a general error
1350      * @throws ClassNotFoundException if the decoded object is of a 
1351      *         class that cannot be found by the JVM
1352      * @since 2.3.4
1353      */
1354     public static Object decodeToObject( 
1355     String encodedObject, int options, final ClassLoader loader )
1356     throws java.io.IOException, java.lang.ClassNotFoundException {
1357         
1358         // Decode and gunzip if necessary
1359         byte[] objBytes = decode( encodedObject, options );
1360         
1361         java.io.ByteArrayInputStream  bais = null;
1362         java.io.ObjectInputStream     ois  = null;
1363         Object obj = null;
1364         
1365         try {
1366             bais = new java.io.ByteArrayInputStream( objBytes );
1367
1368             // If no custom class loader is provided, use Java's builtin OIS.
1369             if( loader == null ){
1370                 ois  = new java.io.ObjectInputStream( bais );
1371             }   // end if: no loader provided
1372
1373             // Else make a customized object input stream that uses
1374             // the provided class loader.
1375             else {
1376                 ois = new java.io.ObjectInputStream(bais){
1377                     @Override
1378                     public Class<?> resolveClass(java.io.ObjectStreamClass streamClass)
1379                     throws java.io.IOException, ClassNotFoundException {
1380                         Class<?> c = Class.forName(streamClass.getName(), false, loader);
1381                         if( c == null ){
1382                             return super.resolveClass(streamClass);
1383                         } else {
1384                             return c;   // Class loader knows of this class.
1385                         }   // end else: not null
1386                     }   // end resolveClass
1387                 };  // end ois
1388             }   // end else: no custom class loader
1389         
1390             obj = ois.readObject();
1391         }   // end try
1392         catch( java.io.IOException e ) {
1393             throw e;    // Catch and throw in order to execute finally{}
1394         }   // end catch
1395         catch( java.lang.ClassNotFoundException e ) {
1396             throw e;    // Catch and throw in order to execute finally{}
1397         }   // end catch
1398         finally {
1399             try{ bais.close(); } catch( Exception e ){}
1400             try{ ois.close();  } catch( Exception e ){}
1401         }   // end finally
1402         
1403         return obj;
1404     }   // end decodeObject
1405     
1406     
1407     
1408     /**
1409      * Convenience method for encoding data to a file.
1410      *
1411      * <p>As of v 2.3, if there is a error,
1412      * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
1413      * In earlier versions, it just returned false, but
1414      * in retrospect that's a pretty poor way to handle it.</p>
1415      * 
1416      * @param dataToEncode byte array of data to encode in base64 form
1417      * @param filename Filename for saving encoded data
1418      * @throws java.io.IOException if there is an error
1419      * @throws NullPointerException if dataToEncode is null
1420      * @since 2.1
1421      */
1422     public static void encodeToFile( byte[] dataToEncode, String filename )
1423     throws java.io.IOException {
1424         
1425         if( dataToEncode == null ){
1426             throw new NullPointerException( "Data to encode was null." );
1427         }   // end iff
1428         
1429         Base64.OutputStream bos = null;
1430         try {
1431             bos = new Base64.OutputStream( 
1432                   new java.io.FileOutputStream( filename ), Base64.ENCODE );
1433             bos.write( dataToEncode );
1434         }   // end try
1435         catch( java.io.IOException e ) {
1436             throw e; // Catch and throw to execute finally{} block
1437         }   // end catch: java.io.IOException
1438         finally {
1439             try{ bos.close(); } catch( Exception e ){}
1440         }   // end finally
1441         
1442     }   // end encodeToFile
1443     
1444     
1445     /**
1446      * Convenience method for decoding data to a file.
1447      *
1448      * <p>As of v 2.3, if there is a error,
1449      * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
1450      * In earlier versions, it just returned false, but
1451      * in retrospect that's a pretty poor way to handle it.</p>
1452      * 
1453      * @param dataToDecode Base64-encoded data as a string
1454      * @param filename Filename for saving decoded data
1455      * @throws java.io.IOException if there is an error
1456      * @since 2.1
1457      */
1458     public static void decodeToFile( String dataToDecode, String filename )
1459     throws java.io.IOException {
1460         
1461         Base64.OutputStream bos = null;
1462         try{
1463             bos = new Base64.OutputStream( 
1464                       new java.io.FileOutputStream( filename ), Base64.DECODE );
1465             bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
1466         }   // end try
1467         catch( java.io.IOException e ) {
1468             throw e; // Catch and throw to execute finally{} block
1469         }   // end catch: java.io.IOException
1470         finally {
1471                 try{ bos.close(); } catch( Exception e ){}
1472         }   // end finally
1473         
1474     }   // end decodeToFile
1475     
1476     
1477     
1478     
1479     /**
1480      * Convenience method for reading a base64-encoded
1481      * file and decoding it.
1482      *
1483      * <p>As of v 2.3, if there is a error,
1484      * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
1485      * In earlier versions, it just returned false, but
1486      * in retrospect that's a pretty poor way to handle it.</p>
1487      * 
1488      * @param filename Filename for reading encoded data
1489      * @return decoded byte array
1490      * @throws java.io.IOException if there is an error
1491      * @since 2.1
1492      */
1493     public static byte[] decodeFromFile( String filename )
1494     throws java.io.IOException {
1495         
1496         byte[] decodedData = null;
1497         Base64.InputStream bis = null;
1498         try
1499         {
1500             // Set up some useful variables
1501             java.io.File file = new java.io.File( filename );
1502             byte[] buffer = null;
1503             int length   = 0;
1504             int numBytes = 0;
1505             
1506             // Check for size of file
1507             if( file.length() > Integer.MAX_VALUE )
1508             {
1509                 throw new java.io.IOException( "File is too big for this convenience method (" + file.length() + " bytes)." );
1510             }   // end if: file too big for int index
1511             buffer = new byte[ (int)file.length() ];
1512             
1513             // Open a stream
1514             bis = new Base64.InputStream( 
1515                       new java.io.BufferedInputStream( 
1516                       new java.io.FileInputStream( file ) ), Base64.DECODE );
1517             
1518             // Read until done
1519             while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) {
1520                 length += numBytes;
1521             }   // end while
1522             
1523             // Save in a variable to return
1524             decodedData = new byte[ length ];
1525             System.arraycopy( buffer, 0, decodedData, 0, length );
1526             
1527         }   // end try
1528         catch( java.io.IOException e ) {
1529             throw e; // Catch and release to execute finally{}
1530         }   // end catch: java.io.IOException
1531         finally {
1532             try{ bis.close(); } catch( Exception e) {}
1533         }   // end finally
1534         
1535         return decodedData;
1536     }   // end decodeFromFile
1537     
1538     
1539     
1540     /**
1541      * Convenience method for reading a binary file
1542      * and base64-encoding it.
1543      *
1544      * <p>As of v 2.3, if there is a error,
1545      * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
1546      * In earlier versions, it just returned false, but
1547      * in retrospect that's a pretty poor way to handle it.</p>
1548      * 
1549      * @param filename Filename for reading binary data
1550      * @return base64-encoded string
1551      * @throws java.io.IOException if there is an error
1552      * @since 2.1
1553      */
1554     public static String encodeFromFile( String filename )
1555     throws java.io.IOException {
1556         
1557         String encodedData = null;
1558         Base64.InputStream bis = null;
1559         try
1560         {
1561             // Set up some useful variables
1562             java.io.File file = new java.io.File( filename );
1563             byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4+1),40) ]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5)
1564             int length   = 0;
1565             int numBytes = 0;
1566             
1567             // Open a stream
1568             bis = new Base64.InputStream( 
1569                       new java.io.BufferedInputStream( 
1570                       new java.io.FileInputStream( file ) ), Base64.ENCODE );
1571             
1572             // Read until done
1573             while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) {
1574                 length += numBytes;
1575             }   // end while
1576             
1577             // Save in a variable to return
1578             encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
1579                 
1580         }   // end try
1581         catch( java.io.IOException e ) {
1582             throw e; // Catch and release to execute finally{}
1583         }   // end catch: java.io.IOException
1584         finally {
1585             try{ bis.close(); } catch( Exception e) {}
1586         }   // end finally
1587         
1588         return encodedData;
1589         }   // end encodeFromFile
1590     
1591     /**
1592      * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1593      *
1594      * @param infile Input file
1595      * @param outfile Output file
1596      * @throws java.io.IOException if there is an error
1597      * @since 2.2
1598      */
1599     public static void encodeFileToFile( String infile, String outfile )
1600     throws java.io.IOException {
1601         
1602         String encoded = Base64.encodeFromFile( infile );
1603         java.io.OutputStream out = null;
1604         try{
1605             out = new java.io.BufferedOutputStream(
1606                   new java.io.FileOutputStream( outfile ) );
1607             out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output.
1608         }   // end try
1609         catch( java.io.IOException e ) {
1610             throw e; // Catch and release to execute finally{}
1611         }   // end catch
1612         finally {
1613             try { out.close(); }
1614             catch( Exception ex ){}
1615         }   // end finally    
1616     }   // end encodeFileToFile
1617
1618
1619     /**
1620      * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1621      *
1622      * @param infile Input file
1623      * @param outfile Output file
1624      * @throws java.io.IOException if there is an error
1625      * @since 2.2
1626      */
1627     public static void decodeFileToFile( String infile, String outfile )
1628     throws java.io.IOException {
1629         
1630         byte[] decoded = Base64.decodeFromFile( infile );
1631         java.io.OutputStream out = null;
1632         try{
1633             out = new java.io.BufferedOutputStream(
1634                   new java.io.FileOutputStream( outfile ) );
1635             out.write( decoded );
1636         }   // end try
1637         catch( java.io.IOException e ) {
1638             throw e; // Catch and release to execute finally{}
1639         }   // end catch
1640         finally {
1641             try { out.close(); }
1642             catch( Exception ex ){}
1643         }   // end finally    
1644     }   // end decodeFileToFile
1645     
1646     
1647     /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
1648     
1649     
1650     
1651     /**
1652      * A {@link Base64.InputStream} will read data from another
1653      * <tt>java.io.InputStream</tt>, given in the constructor,
1654      * and encode/decode to/from Base64 notation on the fly.
1655      *
1656      * @see Base64
1657      * @since 1.3
1658      */
1659     public static class InputStream extends java.io.FilterInputStream {
1660         
1661         private boolean encode;         // Encoding or decoding
1662         private int     position;       // Current position in the buffer
1663         private byte[]  buffer;         // Small buffer holding converted data
1664         private int     bufferLength;   // Length of buffer (3 or 4)
1665         private int     numSigBytes;    // Number of meaningful bytes in the buffer
1666         private int     lineLength;
1667         private boolean breakLines;     // Break lines at less than 80 characters
1668         private int     options;        // Record options used to create the stream.
1669         private byte[]  decodabet;      // Local copies to avoid extra method calls
1670         
1671         
1672         /**
1673          * Constructs a {@link Base64.InputStream} in DECODE mode.
1674          *
1675          * @param in the <tt>java.io.InputStream</tt> from which to read data.
1676          * @since 1.3
1677          */
1678         public InputStream( java.io.InputStream in ) {
1679             this( in, DECODE );
1680         }   // end constructor
1681         
1682         
1683         /**
1684          * Constructs a {@link Base64.InputStream} in
1685          * either ENCODE or DECODE mode.
1686          * <p>
1687          * Valid options:<pre>
1688          *   ENCODE or DECODE: Encode or Decode as data is read.
1689          *   DO_BREAK_LINES: break lines at 76 characters
1690          *     (only meaningful when encoding)</i>
1691          * </pre>
1692          * <p>
1693          * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1694          *
1695          *
1696          * @param in the <tt>java.io.InputStream</tt> from which to read data.
1697          * @param options Specified options
1698          * @see Base64#ENCODE
1699          * @see Base64#DECODE
1700          * @see Base64#DO_BREAK_LINES
1701          * @since 2.0
1702          */
1703         public InputStream( java.io.InputStream in, int options ) {
1704             
1705             super( in );
1706             this.options      = options; // Record for later
1707             this.breakLines   = (options & DO_BREAK_LINES) > 0;
1708             this.encode       = (options & ENCODE) > 0;
1709             this.bufferLength = encode ? 4 : 3;
1710             this.buffer       = new byte[ bufferLength ];
1711             this.position     = -1;
1712             this.lineLength   = 0;
1713             this.decodabet    = getDecodabet(options);
1714         }   // end constructor
1715         
1716         /**
1717          * Reads enough of the input stream to convert
1718          * to/from Base64 and returns the next byte.
1719          *
1720          * @return next byte
1721          * @since 1.3
1722          */
1723         @Override
1724         public int read() throws java.io.IOException  {
1725             
1726             // Do we need to get data?
1727             if( position < 0 ) {
1728                 if( encode ) {
1729                     byte[] b3 = new byte[3];
1730                     int numBinaryBytes = 0;
1731                     for( int i = 0; i < 3; i++ ) {
1732                         int b = in.read();
1733
1734                         // If end of stream, b is -1.
1735                         if( b >= 0 ) {
1736                             b3[i] = (byte)b;
1737                             numBinaryBytes++;
1738                         } else {
1739                             break; // out of for loop
1740                         }   // end else: end of stream
1741                             
1742                     }   // end for: each needed input byte
1743                     
1744                     if( numBinaryBytes > 0 ) {
1745                         encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
1746                         position = 0;
1747                         numSigBytes = 4;
1748                     }   // end if: got data
1749                     else {
1750                         return -1;  // Must be end of stream
1751                     }   // end else
1752                 }   // end if: encoding
1753                 
1754                 // Else decoding
1755                 else {
1756                     byte[] b4 = new byte[4];
1757                     int i = 0;
1758                     for( i = 0; i < 4; i++ ) {
1759                         // Read four "meaningful" bytes:
1760                         int b = 0;
1761                         do{ b = in.read(); }
1762                         while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
1763                         
1764                         if( b < 0 ) {
1765                             break; // Reads a -1 if end of stream
1766                         }   // end if: end of stream
1767                         
1768                         b4[i] = (byte)b;
1769                     }   // end for: each needed input byte
1770                     
1771                     if( i == 4 ) {
1772                         numSigBytes = decode4to3( b4, 0, buffer, 0, options );
1773                         position = 0;
1774                     }   // end if: got four characters
1775                     else if( i == 0 ){
1776                         return -1;
1777                     }   // end else if: also padded correctly
1778                     else {
1779                         // Must have broken out from above.
1780                         throw new java.io.IOException( "Improperly padded Base64 input." );
1781                     }   // end 
1782                     
1783                 }   // end else: decode
1784             }   // end else: get data
1785             
1786             // Got data?
1787             if( position >= 0 ) {
1788                 // End of relevant data?
1789                 if( /*!encode &&*/ position >= numSigBytes ){
1790                     return -1;
1791                 }   // end if: got data
1792                 
1793                 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) {
1794                     lineLength = 0;
1795                     return '\n';
1796                 }   // end if
1797                 else {
1798                     lineLength++;   // This isn't important when decoding
1799                                     // but throwing an extra "if" seems
1800                                     // just as wasteful.
1801                     
1802                     int b = buffer[ position++ ];
1803
1804                     if( position >= bufferLength ) {
1805                         position = -1;
1806                     }   // end if: end
1807
1808                     return b & 0xFF; // This is how you "cast" a byte that's
1809                                      // intended to be unsigned.
1810                 }   // end else
1811             }   // end if: position >= 0
1812             
1813             // Else error
1814             else {
1815                 throw new java.io.IOException( "Error in Base64 code reading stream." );
1816             }   // end else
1817         }   // end read
1818         
1819         
1820         /**
1821          * Calls {@link #read()} repeatedly until the end of stream
1822          * is reached or <var>len</var> bytes are read.
1823          * Returns number of bytes read into array or -1 if
1824          * end of stream is encountered.
1825          *
1826          * @param dest array to hold values
1827          * @param off offset for array
1828          * @param len max number of bytes to read into array
1829          * @return bytes read into array or -1 if end of stream is encountered.
1830          * @since 1.3
1831          */
1832         @Override
1833         public int read( byte[] dest, int off, int len ) 
1834         throws java.io.IOException {
1835             int i;
1836             int b;
1837             for( i = 0; i < len; i++ ) {
1838                 b = read();
1839                 
1840                 if( b >= 0 ) {
1841                     dest[off + i] = (byte) b;
1842                 }
1843                 else if( i == 0 ) {
1844                     return -1;
1845                 }
1846                 else {
1847                     break; // Out of 'for' loop
1848                 } // Out of 'for' loop
1849             }   // end for: each byte read
1850             return i;
1851         }   // end read
1852         
1853     }   // end inner class InputStream
1854     
1855     
1856     
1857     
1858     
1859     
1860     /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
1861     
1862     
1863     
1864     /**
1865      * A {@link Base64.OutputStream} will write data to another
1866      * <tt>java.io.OutputStream</tt>, given in the constructor,
1867      * and encode/decode to/from Base64 notation on the fly.
1868      *
1869      * @see Base64
1870      * @since 1.3
1871      */
1872     public static class OutputStream extends java.io.FilterOutputStream {
1873         
1874         private boolean encode;
1875         private int     position;
1876         private byte[]  buffer;
1877         private int     bufferLength;
1878         private int     lineLength;
1879         private boolean breakLines;
1880         private byte[]  b4;         // Scratch used in a few places
1881         private boolean suspendEncoding;
1882         private int     options;    // Record for later
1883         private byte[]  decodabet;  // Local copies to avoid extra method calls
1884         
1885         /**
1886          * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1887          *
1888          * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1889          * @since 1.3
1890          */
1891         public OutputStream( java.io.OutputStream out ) {
1892             this( out, ENCODE );
1893         }   // end constructor
1894         
1895         
1896         /**
1897          * Constructs a {@link Base64.OutputStream} in
1898          * either ENCODE or DECODE mode.
1899          * <p>
1900          * Valid options:<pre>
1901          *   ENCODE or DECODE: Encode or Decode as data is read.
1902          *   DO_BREAK_LINES: don't break lines at 76 characters
1903          *     (only meaningful when encoding)</i>
1904          * </pre>
1905          * <p>
1906          * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1907          *
1908          * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1909          * @param options Specified options.
1910          * @see Base64#ENCODE
1911          * @see Base64#DECODE
1912          * @see Base64#DO_BREAK_LINES
1913          * @since 1.3
1914          */
1915         public OutputStream( java.io.OutputStream out, int options ) {
1916             super( out );
1917             this.breakLines   = (options & DO_BREAK_LINES) != 0;
1918             this.encode       = (options & ENCODE) != 0;
1919             this.bufferLength = encode ? 3 : 4;
1920             this.buffer       = new byte[ bufferLength ];
1921             this.position     = 0;
1922             this.lineLength   = 0;
1923             this.suspendEncoding = false;
1924             this.b4           = new byte[4];
1925             this.options      = options;
1926             this.decodabet    = getDecodabet(options);
1927         }   // end constructor
1928         
1929         
1930         /**
1931          * Writes the byte to the output stream after
1932          * converting to/from Base64 notation.
1933          * When encoding, bytes are buffered three
1934          * at a time before the output stream actually
1935          * gets a write() call.
1936          * When decoding, bytes are buffered four
1937          * at a time.
1938          *
1939          * @param theByte the byte to write
1940          * @since 1.3
1941          */
1942         @Override
1943         public void write(int theByte) 
1944         throws java.io.IOException {
1945             // Encoding suspended?
1946             if( suspendEncoding ) {
1947                 this.out.write( theByte );
1948                 return;
1949             }   // end if: supsended
1950             
1951             // Encode?
1952             if( encode ) {
1953                 buffer[ position++ ] = (byte)theByte;
1954                 if( position >= bufferLength ) { // Enough to encode.
1955                 
1956                     this.out.write( encode3to4( b4, buffer, bufferLength, options ) );
1957
1958                     lineLength += 4;
1959                     if( breakLines && lineLength >= MAX_LINE_LENGTH ) {
1960                         this.out.write( NEW_LINE );
1961                         lineLength = 0;
1962                     }   // end if: end of line
1963
1964                     position = 0;
1965                 }   // end if: enough to output
1966             }   // end if: encoding
1967
1968             // Else, Decoding
1969             else {
1970                 // Meaningful Base64 character?
1971                 if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) {
1972                     buffer[ position++ ] = (byte)theByte;
1973                     if( position >= bufferLength ) { // Enough to output.
1974                     
1975                         int len = Base64.decode4to3( buffer, 0, b4, 0, options );
1976                         out.write( b4, 0, len );
1977                         position = 0;
1978                     }   // end if: enough to output
1979                 }   // end if: meaningful base64 character
1980                 else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) {
1981                     throw new java.io.IOException( "Invalid character in Base64 data." );
1982                 }   // end else: not white space either
1983             }   // end else: decoding
1984         }   // end write
1985         
1986         
1987         
1988         /**
1989          * Calls {@link #write(int)} repeatedly until <var>len</var> 
1990          * bytes are written.
1991          *
1992          * @param theBytes array from which to read bytes
1993          * @param off offset for array
1994          * @param len max number of bytes to read into array
1995          * @since 1.3
1996          */
1997         @Override
1998         public void write( byte[] theBytes, int off, int len ) 
1999         throws java.io.IOException {
2000             // Encoding suspended?
2001             if( suspendEncoding ) {
2002                 this.out.write( theBytes, off, len );
2003                 return;
2004             }   // end if: supsended
2005             
2006             for( int i = 0; i < len; i++ ) {
2007                 write( theBytes[ off + i ] );
2008             }   // end for: each byte written
2009             
2010         }   // end write
2011         
2012         
2013         
2014         /**
2015          * Method added by PHIL. [Thanks, PHIL. -Rob]
2016          * This pads the buffer without closing the stream.
2017          * @throws java.io.IOException  if there's an error.
2018          */
2019         public void flushBase64() throws java.io.IOException  {
2020             if( position > 0 ) {
2021                 if( encode ) {
2022                     out.write( encode3to4( b4, buffer, position, options ) );
2023                     position = 0;
2024                 }   // end if: encoding
2025                 else {
2026                     throw new java.io.IOException( "Base64 input not properly padded." );
2027                 }   // end else: decoding
2028             }   // end if: buffer partially full
2029
2030         }   // end flush
2031
2032         
2033         /** 
2034          * Flushes and closes (I think, in the superclass) the stream. 
2035          *
2036          * @since 1.3
2037          */
2038         @Override
2039         public void close() throws java.io.IOException {
2040             // 1. Ensure that pending characters are written
2041             flushBase64();
2042
2043             // 2. Actually close the stream
2044             // Base class both flushes and closes.
2045             super.close();
2046             
2047             buffer = null;
2048             out    = null;
2049         }   // end close
2050         
2051         
2052         
2053         /**
2054          * Suspends encoding of the stream.
2055          * May be helpful if you need to embed a piece of
2056          * base64-encoded data in a stream.
2057          *
2058          * @throws java.io.IOException  if there's an error flushing
2059          * @since 1.5.1
2060          */
2061         public void suspendEncoding() throws java.io.IOException  {
2062             flushBase64();
2063             this.suspendEncoding = true;
2064         }   // end suspendEncoding
2065         
2066         
2067         /**
2068          * Resumes encoding of the stream.
2069          * May be helpful if you need to embed a piece of
2070          * base64-encoded data in a stream.
2071          *
2072          * @since 1.5.1
2073          */
2074         public void resumeEncoding() {
2075             this.suspendEncoding = false;
2076         }   // end resumeEncoding
2077         
2078         
2079         
2080     }   // end inner class OutputStream
2081     
2082     
2083 }   // end class Base64