This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 219117
Collapse All | Expand All

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

Return to bug 219117