Statistics
| Revision:

root / branches / Mobile_Compatible_Hito_1 / libFMap / src / es / prodevelop / gvsig / mobile / fmap / driver / raster / google / satellite / Base64.java @ 21606

History | View | Annotate | Download (69.3 KB)

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>The <tt>options</tt> parameter, which appears in a few places, is used to pass 
6
 * several pieces of information to the encoder. In the "higher level" methods such as 
7
 * encodeBytes( bytes, options ) the options parameter can be used to indicate such 
8
 * things as first gzipping the bytes before encoding them, not inserting linefeeds 
9
 * (though that breaks strict Base64 compatibility), and encoding using the URL-safe 
10
 * and Ordered dialects.</p>
11
 *
12
 * <p>The constants defined in Base64 can be OR-ed together to combine options, so you 
13
 * might make a call like this:</p>
14
 *
15
 * <code>String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DONT_BREAK_LINES );</code>
16
 *
17
 * <p>to compress the data before encoding it and then making the output have no newline characters.</p>
18
 *
19
 *
20
 * <p>
21
 * Change Log:
22
 * </p>
23
 * <ul>
24
 *  <li>v2.2.2 - Fixed encodeFileToFile and decodeFileToFile to use the
25
 *   Base64.InputStream class to encode and decode on the fly which uses
26
 *   less memory than encoding/decoding an entire file into memory before writing.</li>
27
 *  <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug
28
 *   when using very small files (~< 40 bytes).</li>
29
 *  <li>v2.2 - Added some helper methods for encoding/decoding directly from
30
 *   one file to the next. Also added a main() method to support command line
31
 *   encoding/decoding from one file to the next. Also added these Base64 dialects:
32
 *   <ol>
33
 *   <li>The default is RFC3548 format.</li>
34
 *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates
35
 *   URL and file name friendly format as described in Section 4 of RFC3548.
36
 *   http://www.faqs.org/rfcs/rfc3548.html</li>
37
 *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates
38
 *   URL and file name friendly format that preserves lexical ordering as described
39
 *   in http://www.faqs.org/qa/rfcc-1940.html</li>
40
 *   </ol>
41
 *   Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a>
42
 *   for contributing the new Base64 dialects.
43
 *  </li>
44
 * 
45
 *  <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
46
 *   some convenience methods for reading and writing to and from files.</li>
47
 *  <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
48
 *   with other encodings (like EBCDIC).</li>
49
 *  <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
50
 *   encoded data was a single byte.</li>
51
 *  <li>v2.0 - I got rid of methods that used booleans to set options. 
52
 *   Now everything is more consolidated and cleaner. The code now detects
53
 *   when data that's being decoded is gzip-compressed and will decompress it
54
 *   automatically. Generally things are cleaner. You'll probably have to
55
 *   change some method calls that you were making to support the new
56
 *   options format (<tt>int</tt>s that you "OR" together).</li>
57
 *  <li>v1.5.1 - Fixed bug when decompressing and decoding to a             
58
 *   byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.      
59
 *   Added the ability to "suspend" encoding in the Output Stream so        
60
 *   you can turn on and off the encoding if you need to embed base64       
61
 *   data in an otherwise "normal" stream (like an XML file).</li>  
62
 *  <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
63
 *      This helps when using GZIP streams.
64
 *      Added the ability to GZip-compress objects before encoding them.</li>
65
 *  <li>v1.4 - Added helper methods to read/write files.</li>
66
 *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
67
 *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
68
 *      where last buffer being read, if not completely full, was not returned.</li>
69
 *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
70
 *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
71
 * </ul>
72
 *
73
 * <p>
74
 * I am placing this code in the Public Domain. Do with it as you will.
75
 * This software comes with no guarantees or warranties but with
76
 * plenty of well-wishing instead!
77
 * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
78
 * periodically to check for updates or to contribute improvements.
79
 * </p>
80
 *
81
 * @author Robert Harder
82
 * @author rob@iharder.net
83
 * @version 2.2.2
84
 */
85
/************************************************
86
 *                                                                                                *
87
 *   Modfied By:                                                                *
88
 *   Prodevelop Integraci?n de Tecnolog?as SL        *
89
 *   Conde Salvatierra de ?lava , 34-10                        *
90
 *   46004 Valencia                                                                *
91
 *   Spain                                                                                *
92
 *                                                                                                *
93
 *   +34 963 510 612                                                        *
94
 *   +34 963 510 968                                                        *
95
 *   gis@prodevelop.es                                                        *
96
 *   http://www.prodevelop.es                                        *
97
 *                                                                                                *
98
 *   gvSIG Mobile Team 2006                                         *
99
 *                                                                                          *         
100
 ************************************************/
101

    
102
package es.prodevelop.gvsig.mobile.fmap.driver.raster.google.satellite;
103

    
104
public class Base64
105
{
106
    
107
/* ********  P U B L I C   F I E L D S  ******** */   
108
    
109
    
110
    /** No options specified. Value is zero. */
111
    public final static int NO_OPTIONS = 0;
112
    
113
    /** Specify encoding. */
114
    public final static int ENCODE = 1;
115
    
116
    
117
    /** Specify decoding. */
118
    public final static int DECODE = 0;
119
    
120
    
121
    /** Specify that data should be gzip-compressed. */
122
    public final static int GZIP = 2;
123
    
124
    
125
    /** Don't break lines when encoding (violates strict Base64 specification) */
126
    public final static int DONT_BREAK_LINES = 8;
127
        
128
        /** 
129
         * Encode using Base64-like encoding that is URL- and Filename-safe as described
130
         * in Section 4 of RFC3548: 
131
         * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
132
         * It is important to note that data encoded this way is <em>not</em> officially valid Base64, 
133
         * or at the very least should not be called Base64 without also specifying that is
134
         * was encoded using the URL- and Filename-safe dialect.
135
         */
136
         public final static int URL_SAFE = 16;
137
         
138
         
139
         /**
140
          * Encode using the special "ordered" dialect of Base64 described here:
141
          * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
142
          */
143
         public final static int ORDERED = 32;
144
    
145
    
146
/* ********  P R I V A T E   F I E L D S  ******** */  
147
    
148
    
149
    /** Maximum line length (76) of Base64 output. */
150
    private final static int MAX_LINE_LENGTH = 76;
151
    
152
    
153
    /** The equals sign (=) as a byte. */
154
    private final static byte EQUALS_SIGN = (byte)'=';
155
    
156
    
157
    /** The new line character (\n) as a byte. */
158
    private final static byte NEW_LINE = (byte)'\n';
159
    
160
    
161
    /** Preferred encoding. */
162
    private final static String PREFERRED_ENCODING = "UTF-8";
163
    
164
        
165
    // I think I end up not using the BAD_ENCODING indicator.
166
    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
167
    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
168
    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
169
        
170
        
171
/* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */        
172
    
173
    /** The 64 valid Base64 values. */
174
    //private final static byte[] ALPHABET;
175
        /* Host platform me be something funny like EBCDIC, so we hardcode these values. */
176
        private final static byte[] _STANDARD_ALPHABET =
177
    {
178
        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
179
        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
180
        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
181
        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
182
        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
183
        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
184
        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
185
        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
186
        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
187
        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
188
    };
189
        
190
    
191
    /** 
192
     * Translates a Base64 value to either its 6-bit reconstruction value
193
     * or a negative number indicating some other meaning.
194
     **/
195
    private final static byte[] _STANDARD_DECODABET =
196
    {   
197
        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
198
        -5,-5,                                      // Whitespace: Tab and Linefeed
199
        -9,-9,                                      // Decimal 11 - 12
200
        -5,                                         // Whitespace: Carriage Return
201
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
202
        -9,-9,-9,-9,-9,                             // Decimal 27 - 31
203
        -5,                                         // Whitespace: Space
204
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
205
        62,                                         // Plus sign at decimal 43
206
        -9,-9,-9,                                   // Decimal 44 - 46
207
        63,                                         // Slash at decimal 47
208
        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
209
        -9,-9,-9,                                   // Decimal 58 - 60
210
        -1,                                         // Equals sign at decimal 61
211
        -9,-9,-9,                                      // Decimal 62 - 64
212
        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
213
        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
214
        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
215
        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
216
        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
217
        -9,-9,-9,-9                                 // Decimal 123 - 126
218
        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
219
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
220
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
221
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
222
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
223
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
224
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
225
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
226
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
227
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
228
    };
229
        
230
        
231
/* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */
232
        
233
        /**
234
         * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: 
235
         * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
236
         * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
237
         */
238
    private final static byte[] _URL_SAFE_ALPHABET =
239
    {
240
      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
241
      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
242
      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
243
      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
244
      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
245
      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
246
      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
247
      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
248
      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
249
      (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
250
    };
251
        
252
        /**
253
         * Used in decoding URL- and Filename-safe dialects of Base64.
254
         */
255
    private final static byte[] _URL_SAFE_DECODABET =
256
    {   
257
      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
258
      -5,-5,                                      // Whitespace: Tab and Linefeed
259
      -9,-9,                                      // Decimal 11 - 12
260
      -5,                                         // Whitespace: Carriage Return
261
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
262
      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
263
      -5,                                         // Whitespace: Space
264
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
265
      -9,                                         // Plus sign at decimal 43
266
      -9,                                         // Decimal 44
267
      62,                                         // Minus sign at decimal 45
268
      -9,                                         // Decimal 46
269
      -9,                                         // Slash at decimal 47
270
      52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
271
      -9,-9,-9,                                   // Decimal 58 - 60
272
      -1,                                         // Equals sign at decimal 61
273
      -9,-9,-9,                                   // Decimal 62 - 64
274
      0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
275
      14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
276
      -9,-9,-9,-9,                                // Decimal 91 - 94
277
      63,                                         // Underscore at decimal 95
278
      -9,                                         // Decimal 96
279
      26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
280
      39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
281
      -9,-9,-9,-9                                 // Decimal 123 - 126
282
      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
283
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
284
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
285
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
286
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
287
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
288
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
289
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
290
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
291
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
292
    };
293

    
294

    
295

    
296
/* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */
297

    
298
        /**
299
         * I don't get the point of this technique, but it is described here:
300
         * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
301
         */
302
    private final static byte[] _ORDERED_ALPHABET =
303
    {
304
      (byte)'-',
305
      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
306
      (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
307
      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
308
      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
309
      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
310
      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
311
      (byte)'_',
312
      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
313
      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
314
      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
315
      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
316
    };
317
        
318
        /**
319
         * Used in decoding the "ordered" dialect of Base64.
320
         */
321
    private final static byte[] _ORDERED_DECODABET =
322
    {   
323
      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
324
      -5,-5,                                      // Whitespace: Tab and Linefeed
325
      -9,-9,                                      // Decimal 11 - 12
326
      -5,                                         // Whitespace: Carriage Return
327
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
328
      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
329
      -5,                                         // Whitespace: Space
330
      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
331
      -9,                                         // Plus sign at decimal 43
332
      -9,                                         // Decimal 44
333
      0,                                          // Minus sign at decimal 45
334
      -9,                                         // Decimal 46
335
      -9,                                         // Slash at decimal 47
336
      1,2,3,4,5,6,7,8,9,10,                       // Numbers zero through nine
337
      -9,-9,-9,                                   // Decimal 58 - 60
338
      -1,                                         // Equals sign at decimal 61
339
      -9,-9,-9,                                   // Decimal 62 - 64
340
      11,12,13,14,15,16,17,18,19,20,21,22,23,     // Letters 'A' through 'M'
341
      24,25,26,27,28,29,30,31,32,33,34,35,36,     // Letters 'N' through 'Z'
342
      -9,-9,-9,-9,                                // Decimal 91 - 94
343
      37,                                         // Underscore at decimal 95
344
      -9,                                         // Decimal 96
345
      38,39,40,41,42,43,44,45,46,47,48,49,50,     // Letters 'a' through 'm'
346
      51,52,53,54,55,56,57,58,59,60,61,62,63,     // Letters 'n' through 'z'
347
      -9,-9,-9,-9                                 // Decimal 123 - 126
348
      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
349
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
350
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
351
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
352
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
353
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
354
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
355
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
356
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
357
        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
358
    };
359

    
360
        
361
/* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */
362

    
363

    
364
        /**
365
         * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
366
         * the options specified.
367
         * It's possible, though silly, to specify ORDERED and URLSAFE
368
         * in which case one of them will be picked, though there is
369
         * no guarantee as to which one will be picked.
370
         */
371
        private final static byte[] getAlphabet( int options )
372
        {
373
                if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET;
374
                else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET;
375
                else return _STANDARD_ALPHABET;
376
                
377
        }        // end getAlphabet
378
        
379
        
380
        /**
381
         * Returns one of the _SOMETHING_DECODABET byte arrays depending on
382
         * the options specified.
383
         * It's possible, though silly, to specify ORDERED and URL_SAFE
384
         * in which case one of them will be picked, though there is
385
         * no guarantee as to which one will be picked.
386
         */
387
        private final static byte[] getDecodabet( int options )
388
        {
389
                if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET;
390
                else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET;
391
                else return _STANDARD_DECODABET;
392
                
393
        }        // end getAlphabet
394
        
395

    
396
    
397
    /** Defeats instantiation. */
398
    private Base64(){}
399
    
400

    
401
    /**
402
     * Encodes or decodes two files from the command line;
403
     * <strong>feel free to delete this method (in fact you probably should)
404
     * if you're embedding this code into a larger program.</strong>
405
     */
406
    public final static void main( String[] args )
407
    {
408
        if( args.length < 3 ){
409
            usage("Not enough arguments.");
410
        }   // end if: args.length < 3
411
        else {
412
            String flag = args[0];
413
            String infile = args[1];
414
            String outfile = args[2];
415
            if( flag.equals( "-e" ) ){
416
                Base64.encodeFileToFile( infile, outfile );
417
            }   // end if: encode
418
            else if( flag.equals( "-d" ) ) {
419
                Base64.decodeFileToFile( infile, outfile );
420
            }   // end else if: decode    
421
            else {
422
                usage( "Unknown flag: " + flag );
423
            }   // end else    
424
        }   // end else
425
    }   // end main
426

    
427
    /**
428
     * Prints command line usage.
429
     *
430
     * @param msg A message to include with usage info.
431
     */
432
    private final static void usage( String msg )
433
    {
434
        System.err.println( msg );
435
        System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" );
436
    }   // end usage
437
    
438
    
439
/* ********  E N C O D I N G   M E T H O D S  ******** */    
440
    
441
    
442
    /**
443
     * Encodes up to the first three bytes of array <var>threeBytes</var>
444
     * and returns a four-byte array in Base64 notation.
445
     * The actual number of significant bytes in your array is
446
     * given by <var>numSigBytes</var>.
447
     * The array <var>threeBytes</var> needs only be as big as
448
     * <var>numSigBytes</var>.
449
     * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
450
     *
451
     * @param b4 A reusable byte array to reduce array instantiation
452
     * @param threeBytes the array to convert
453
     * @param numSigBytes the number of significant bytes in your array
454
     * @return four byte array in Base64 notation.
455
     * @since 1.5.1
456
     */
457
    private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options )
458
    {
459
        encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
460
        return b4;
461
    }   // end encode3to4
462

    
463
    
464
    /**
465
     * <p>Encodes up to three bytes of the array <var>source</var>
466
     * and writes the resulting four Base64 bytes to <var>destination</var>.
467
     * The source and destination arrays can be manipulated
468
     * anywhere along their length by specifying 
469
     * <var>srcOffset</var> and <var>destOffset</var>.
470
     * This method does not check to make sure your arrays
471
     * are large enough to accomodate <var>srcOffset</var> + 3 for
472
     * the <var>source</var> array or <var>destOffset</var> + 4 for
473
     * the <var>destination</var> array.
474
     * The actual number of significant bytes in your array is
475
     * given by <var>numSigBytes</var>.</p>
476
         * <p>This is the lowest level of the encoding methods with
477
         * all possible parameters.</p>
478
     *
479
     * @param source the array to convert
480
     * @param srcOffset the index where conversion begins
481
     * @param numSigBytes the number of significant bytes in your array
482
     * @param destination the array to hold the conversion
483
     * @param destOffset the index where output will be put
484
     * @return the <var>destination</var> array
485
     * @since 1.3
486
     */
487
    private static byte[] encode3to4( 
488
     byte[] source, int srcOffset, int numSigBytes,
489
     byte[] destination, int destOffset, int options )
490
    {
491
                byte[] ALPHABET = getAlphabet( options ); 
492
        
493
        //           1         2         3  
494
        // 01234567890123456789012345678901 Bit position
495
        // --------000000001111111122222222 Array position from threeBytes
496
        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
497
        //          >>18  >>12  >> 6  >> 0  Right shift necessary
498
        //                0x3f  0x3f  0x3f  Additional AND
499
        
500
        // Create buffer with zero-padding if there are only one or two
501
        // significant bytes passed in the array.
502
        // We have to shift left 24 in order to flush out the 1's that appear
503
        // when Java treats a value as negative that is cast from a byte to an int.
504
        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
505
                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
506
                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
507

    
508
        switch( numSigBytes )
509
        {
510
            case 3:
511
                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
512
                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
513
                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
514
                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
515
                return destination;
516
                
517
            case 2:
518
                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
519
                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
520
                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
521
                destination[ destOffset + 3 ] = EQUALS_SIGN;
522
                return destination;
523
                
524
            case 1:
525
                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
526
                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
527
                destination[ destOffset + 2 ] = EQUALS_SIGN;
528
                destination[ destOffset + 3 ] = EQUALS_SIGN;
529
                return destination;
530
                
531
            default:
532
                return destination;
533
        }   // end switch
534
    }   // end encode3to4
535
    
536
    
537
    
538
    /**
539
     * Serializes an object and returns the Base64-encoded
540
     * version of that serialized object. If the object
541
     * cannot be serialized or there is another error,
542
     * the method will return <tt>null</tt>.
543
     * The object is not GZip-compressed before being encoded.
544
     *
545
     * @param serializableObject The object to encode
546
     * @return The Base64-encoded object
547
     * @since 1.4
548
     */
549
    public static String encodeObject( java.io.Serializable serializableObject )
550
    {
551
        return encodeObject( serializableObject, NO_OPTIONS );
552
    }   // end encodeObject
553
    
554

    
555

    
556
    /**
557
     * Serializes an object and returns the Base64-encoded
558
     * version of that serialized object. If the object
559
     * cannot be serialized or there is another error,
560
     * the method will return <tt>null</tt>.
561
     * <p>
562
     * Valid options:<pre>
563
     *   GZIP: gzip-compresses object before encoding it.
564
     *   DONT_BREAK_LINES: don't break lines at 76 characters
565
     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
566
     * </pre>
567
     * <p>
568
     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
569
     * <p>
570
     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
571
     *
572
     * @param serializableObject The object to encode
573
     * @param options Specified options
574
     * @return The Base64-encoded object
575
     * @see Base64#GZIP
576
     * @see Base64#DONT_BREAK_LINES
577
     * @since 2.0
578
     */
579
    public static String encodeObject( java.io.Serializable serializableObject, int options )
580
    {
581
        // Streams
582
        java.io.ByteArrayOutputStream  baos  = null; 
583
        java.io.OutputStream           b64os = null; 
584
        java.io.ObjectOutputStream     oos   = null; 
585
        java.util.zip.GZIPOutputStream gzos  = null;
586
        
587
        // Isolate options
588
        int gzip           = (options & GZIP);
589
        int dontBreakLines = (options & DONT_BREAK_LINES);
590
        
591
        try
592
        {
593
            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
594
            baos  = new java.io.ByteArrayOutputStream();
595
            b64os = new Base64.OutputStream( baos, ENCODE | options );
596
    
597
            // GZip?
598
            if( gzip == GZIP )
599
            {
600
                gzos = new java.util.zip.GZIPOutputStream( b64os );
601
                oos  = new java.io.ObjectOutputStream( gzos );
602
            }   // end if: gzip
603
            else
604
                oos   = new java.io.ObjectOutputStream( b64os );
605
            
606
            oos.writeObject( serializableObject );
607
        }   // end try
608
        catch( java.io.IOException e )
609
        {
610
            e.printStackTrace();
611
            return null;
612
        }   // end catch
613
        finally
614
        {
615
            try{ oos.close();   } catch( Exception e ){}
616
            try{ gzos.close();  } catch( Exception e ){}
617
            try{ b64os.close(); } catch( Exception e ){}
618
            try{ baos.close();  } catch( Exception e ){}
619
        }   // end finally
620
        
621
        // Return value according to relevant encoding.
622
        try 
623
        {
624
            return new String( baos.toByteArray(), PREFERRED_ENCODING );
625
        }   // end try
626
        catch (java.io.UnsupportedEncodingException uue)
627
        {
628
            return new String( baos.toByteArray() );
629
        }   // end catch
630
        
631
    }   // end encode
632
    
633
    
634

    
635
    /**
636
     * Encodes a byte array into Base64 notation.
637
     * Does not GZip-compress data.
638
     *
639
     * @param source The data to convert
640
     * @since 1.4
641
     */
642
    public static String encodeBytes( byte[] source )
643
    {
644
        return encodeBytes( source, 0, source.length, NO_OPTIONS );
645
    }   // end encodeBytes
646
    
647

    
648

    
649
    /**
650
     * Encodes a byte array into Base64 notation.
651
     * <p>
652
     * Valid options:<pre>
653
     *   GZIP: gzip-compresses object before encoding it.
654
     *   DONT_BREAK_LINES: don't break lines at 76 characters
655
     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
656
     * </pre>
657
     * <p>
658
     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
659
     * <p>
660
     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
661
     *
662
     *
663
     * @param source The data to convert
664
     * @param options Specified options
665
     * @see Base64#GZIP
666
     * @see Base64#DONT_BREAK_LINES
667
     * @since 2.0
668
     */
669
    public static String encodeBytes( byte[] source, int options )
670
    {   
671
        return encodeBytes( source, 0, source.length, options );
672
    }   // end encodeBytes
673
    
674
    
675
    /**
676
     * Encodes a byte array into Base64 notation.
677
     * Does not GZip-compress data.
678
     *
679
     * @param source The data to convert
680
     * @param off Offset in array where conversion should begin
681
     * @param len Length of data to convert
682
     * @since 1.4
683
     */
684
    public static String encodeBytes( byte[] source, int off, int len )
685
    {
686
        return encodeBytes( source, off, len, NO_OPTIONS );
687
    }   // end encodeBytes
688
    
689
    
690

    
691
    /**
692
     * Encodes a byte array into Base64 notation.
693
     * <p>
694
     * Valid options:<pre>
695
     *   GZIP: gzip-compresses object before encoding it.
696
     *   DONT_BREAK_LINES: don't break lines at 76 characters
697
     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
698
     * </pre>
699
     * <p>
700
     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
701
     * <p>
702
     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
703
     *
704
     *
705
     * @param source The data to convert
706
     * @param off Offset in array where conversion should begin
707
     * @param len Length of data to convert
708
     * @param options Specified options
709
         * @param options alphabet type is pulled from this (standard, url-safe, ordered)
710
     * @see Base64#GZIP
711
     * @see Base64#DONT_BREAK_LINES
712
     * @since 2.0
713
     */
714
    public static String encodeBytes( byte[] source, int off, int len, int options )
715
    {
716
        // Isolate options
717
        int dontBreakLines = ( options & DONT_BREAK_LINES );
718
        int gzip           = ( options & GZIP   );
719
        
720
        // Compress?
721
        if( gzip == GZIP )
722
        {
723
            java.io.ByteArrayOutputStream  baos  = null;
724
            java.util.zip.GZIPOutputStream gzos  = null;
725
            Base64.OutputStream            b64os = null;
726
            
727
    
728
            try
729
            {
730
                // GZip -> Base64 -> ByteArray
731
                baos = new java.io.ByteArrayOutputStream();
732
                b64os = new Base64.OutputStream( baos, ENCODE | options );
733
                gzos  = new java.util.zip.GZIPOutputStream( b64os ); 
734
            
735
                gzos.write( source, off, len );
736
                gzos.close();
737
            }   // end try
738
            catch( java.io.IOException e )
739
            {
740
                e.printStackTrace();
741
                return null;
742
            }   // end catch
743
            finally
744
            {
745
                try{ gzos.close();  } catch( Exception e ){}
746
                try{ b64os.close(); } catch( Exception e ){}
747
                try{ baos.close();  } catch( Exception e ){}
748
            }   // end finally
749

    
750
            // Return value according to relevant encoding.
751
            try
752
            {
753
                return new String( baos.toByteArray(), PREFERRED_ENCODING );
754
            }   // end try
755
            catch (java.io.UnsupportedEncodingException uue)
756
            {
757
                return new String( baos.toByteArray() );
758
            }   // end catch
759
        }   // end if: compress
760
        
761
        // Else, don't compress. Better not to use streams at all then.
762
        else
763
        {
764
            // Convert option to boolean in way that code likes it.
765
            boolean breakLines = dontBreakLines == 0;
766
            
767
            int    len43   = len * 4 / 3;
768
            byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
769
                                       + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
770
                                       + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines      
771
            int d = 0;
772
            int e = 0;
773
            int len2 = len - 2;
774
            int lineLength = 0;
775
            for( ; d < len2; d+=3, e+=4 )
776
            {
777
                encode3to4( source, d+off, 3, outBuff, e, options );
778

    
779
                lineLength += 4;
780
                if( breakLines && lineLength == MAX_LINE_LENGTH )
781
                {   
782
                    outBuff[e+4] = NEW_LINE;
783
                    e++;
784
                    lineLength = 0;
785
                }   // end if: end of line
786
            }   // en dfor: each piece of array
787

    
788
            if( d < len )
789
            {
790
                encode3to4( source, d+off, len - d, outBuff, e, options );
791
                e += 4;
792
            }   // end if: some padding needed
793

    
794
            
795
            // Return value according to relevant encoding.
796
            try
797
            {
798
                return new String( outBuff, 0, e, PREFERRED_ENCODING );
799
            }   // end try
800
            catch (java.io.UnsupportedEncodingException uue)
801
            {
802
                return new String( outBuff, 0, e );
803
            }   // end catch
804
            
805
        }   // end else: don't compress
806
        
807
    }   // end encodeBytes
808
    
809

    
810
    
811
    
812
    
813
/* ********  D E C O D I N G   M E T H O D S  ******** */
814
    
815
    
816
    /**
817
     * Decodes four bytes from array <var>source</var>
818
     * and writes the resulting bytes (up to three of them)
819
     * to <var>destination</var>.
820
     * The source and destination arrays can be manipulated
821
     * anywhere along their length by specifying 
822
     * <var>srcOffset</var> and <var>destOffset</var>.
823
     * This method does not check to make sure your arrays
824
     * are large enough to accomodate <var>srcOffset</var> + 4 for
825
     * the <var>source</var> array or <var>destOffset</var> + 3 for
826
     * the <var>destination</var> array.
827
     * This method returns the actual number of bytes that 
828
     * were converted from the Base64 encoding.
829
         * <p>This is the lowest level of the decoding methods with
830
         * all possible parameters.</p>
831
     * 
832
     *
833
     * @param source the array to convert
834
     * @param srcOffset the index where conversion begins
835
     * @param destination the array to hold the conversion
836
     * @param destOffset the index where output will be put
837
         * @param options alphabet type is pulled from this (standard, url-safe, ordered)
838
     * @return the number of decoded bytes converted
839
     * @since 1.3
840
     */
841
    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options )
842
    {
843
                byte[] DECODABET = getDecodabet( options ); 
844
        
845
        // Example: Dk==
846
        if( source[ srcOffset + 2] == EQUALS_SIGN )
847
        {
848
            // Two ways to do the same thing. Don't know which way I like best.
849
            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
850
            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
851
            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
852
                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
853
            
854
            destination[ destOffset ] = (byte)( outBuff >>> 16 );
855
            return 1;
856
        }
857
        
858
        // Example: DkL=
859
        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
860
        {
861
            // Two ways to do the same thing. Don't know which way I like best.
862
            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
863
            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
864
            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
865
            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
866
                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
867
                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
868
            
869
            destination[ destOffset     ] = (byte)( outBuff >>> 16 );
870
            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
871
            return 2;
872
        }
873
        
874
        // Example: DkLE
875
        else
876
        {
877
            try{
878
            // Two ways to do the same thing. Don't know which way I like best.
879
            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
880
            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
881
            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
882
            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
883
            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
884
                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
885
                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
886
                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
887

    
888
            
889
            destination[ destOffset     ] = (byte)( outBuff >> 16 );
890
            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
891
            destination[ destOffset + 2 ] = (byte)( outBuff       );
892

    
893
            return 3;
894
            }catch( Exception e){
895
                System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
896
                System.out.println(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
897
                System.out.println(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
898
                System.out.println(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
899
                return -1;
900
            }   // end catch
901
        }
902
    }   // end decodeToBytes
903
    
904
    
905
    
906
    
907
    /**
908
     * Very low-level access to decoding ASCII characters in
909
     * the form of a byte array. Does not support automatically
910
     * gunzipping or any other "fancy" features.
911
     *
912
     * @param source The Base64 encoded data
913
     * @param off    The offset of where to begin decoding
914
     * @param len    The length of characters to decode
915
     * @return decoded data
916
     * @since 1.3
917
     */
918
    public static byte[] decode( byte[] source, int off, int len, int options )
919
    {
920
                byte[] DECODABET = getDecodabet( options );
921
        
922
        int    len34   = len * 3 / 4;
923
        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
924
        int    outBuffPosn = 0;
925
        
926
        byte[] b4        = new byte[4];
927
        int    b4Posn    = 0;
928
        int    i         = 0;
929
        byte   sbiCrop   = 0;
930
        byte   sbiDecode = 0;
931
        for( i = off; i < off+len; i++ )
932
        {
933
            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
934
            sbiDecode = DECODABET[ sbiCrop ];
935
            
936
            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
937
            {
938
                if( sbiDecode >= EQUALS_SIGN_ENC )
939
                {
940
                    b4[ b4Posn++ ] = sbiCrop;
941
                    if( b4Posn > 3 )
942
                    {
943
                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
944
                        b4Posn = 0;
945
                        
946
                        // If that was the equals sign, break out of 'for' loop
947
                        if( sbiCrop == EQUALS_SIGN )
948
                            break;
949
                    }   // end if: quartet built
950
                    
951
                }   // end if: equals sign or better
952
                
953
            }   // end if: white space, equals sign or better
954
            else
955
            {
956
                System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
957
                return null;
958
            }   // end else: 
959
        }   // each input character
960
                                   
961
        byte[] out = new byte[ outBuffPosn ];
962
        System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 
963
        return out;
964
    }   // end decode
965
    
966
    
967
        
968
        
969
    /**
970
     * Decodes data from Base64 notation, automatically
971
     * detecting gzip-compressed data and decompressing it.
972
     *
973
     * @param s the string to decode
974
     * @return the decoded data
975
     * @since 1.4
976
     */
977
    public static byte[] decode( String s )
978
        {
979
                return decode( s, NO_OPTIONS );
980
        }
981
    
982
    
983
    /**
984
     * Decodes data from Base64 notation, automatically
985
     * detecting gzip-compressed data and decompressing it.
986
     *
987
     * @param s the string to decode
988
         * @param options encode options such as URL_SAFE
989
     * @return the decoded data
990
     * @since 1.4
991
     */
992
    public static byte[] decode( String s, int options )
993
    {   
994
        byte[] bytes;
995
        try
996
        {
997
            bytes = s.getBytes( PREFERRED_ENCODING );
998
        }   // end try
999
        catch( java.io.UnsupportedEncodingException uee )
1000
        {
1001
            bytes = s.getBytes();
1002
        }   // end catch
1003
                //</change>
1004
        
1005
        // Decode
1006
        bytes = decode( bytes, 0, bytes.length, options );
1007
        
1008
        
1009
        // Check to see if it's gzip-compressed
1010
        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
1011
        if( bytes != null && bytes.length >= 4 )
1012
        {
1013
            
1014
            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);       
1015
            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) 
1016
            {
1017
                java.io.ByteArrayInputStream  bais = null;
1018
                java.util.zip.GZIPInputStream gzis = null;
1019
                java.io.ByteArrayOutputStream baos = null;
1020
                byte[] buffer = new byte[2048];
1021
                int    length = 0;
1022

    
1023
                try
1024
                {
1025
                    baos = new java.io.ByteArrayOutputStream();
1026
                    bais = new java.io.ByteArrayInputStream( bytes );
1027
                    gzis = new java.util.zip.GZIPInputStream( bais );
1028

    
1029
                    while( ( length = gzis.read( buffer ) ) >= 0 )
1030
                    {
1031
                        baos.write(buffer,0,length);
1032
                    }   // end while: reading input
1033

    
1034
                    // No error? Get new bytes.
1035
                    bytes = baos.toByteArray();
1036

    
1037
                }   // end try
1038
                catch( java.io.IOException e )
1039
                {
1040
                    // Just return originally-decoded bytes
1041
                }   // end catch
1042
                finally
1043
                {
1044
                    try{ baos.close(); } catch( Exception e ){}
1045
                    try{ gzis.close(); } catch( Exception e ){}
1046
                    try{ bais.close(); } catch( Exception e ){}
1047
                }   // end finally
1048

    
1049
            }   // end if: gzipped
1050
        }   // end if: bytes.length >= 2
1051
        
1052
        return bytes;
1053
    }   // end decode
1054

    
1055

    
1056
    
1057

    
1058
    /**
1059
     * Attempts to decode Base64 data and deserialize a Java
1060
     * Object within. Returns <tt>null</tt> if there was an error.
1061
     *
1062
     * @param encodedObject The Base64 data to decode
1063
     * @return The decoded and deserialized object
1064
     * @since 1.5
1065
     */
1066
    public static Object decodeToObject( String encodedObject )
1067
    {
1068
        // Decode and gunzip if necessary
1069
        byte[] objBytes = decode( encodedObject );
1070
        
1071
        java.io.ByteArrayInputStream  bais = null;
1072
        java.io.ObjectInputStream     ois  = null;
1073
        Object obj = null;
1074
        
1075
        try
1076
        {
1077
            bais = new java.io.ByteArrayInputStream( objBytes );
1078
            ois  = new java.io.ObjectInputStream( bais );
1079
        
1080
            obj = ois.readObject();
1081
        }   // end try
1082
        catch( java.io.IOException e )
1083
        {
1084
            e.printStackTrace();
1085
            obj = null;
1086
        }   // end catch
1087
        catch( java.lang.ClassNotFoundException e )
1088
        {
1089
            e.printStackTrace();
1090
            obj = null;
1091
        }   // end catch
1092
        finally
1093
        {
1094
            try{ bais.close(); } catch( Exception e ){}
1095
            try{ ois.close();  } catch( Exception e ){}
1096
        }   // end finally
1097
        
1098
        return obj;
1099
    }   // end decodeObject
1100
    
1101
    
1102
    
1103
    /**
1104
     * Convenience method for encoding data to a file.
1105
     *
1106
     * @param dataToEncode byte array of data to encode in base64 form
1107
     * @param filename Filename for saving encoded data
1108
     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
1109
     *
1110
     * @since 2.1
1111
     */
1112
    public static boolean encodeToFile( byte[] dataToEncode, String filename )
1113
    {
1114
        boolean success = false;
1115
        Base64.OutputStream bos = null;
1116
        try
1117
        {
1118
            bos = new Base64.OutputStream( 
1119
                      new java.io.FileOutputStream( filename ), Base64.ENCODE );
1120
            bos.write( dataToEncode );
1121
            success = true;
1122
        }   // end try
1123
        catch( java.io.IOException e )
1124
        {
1125
            
1126
            success = false;
1127
        }   // end catch: IOException
1128
        finally
1129
        {
1130
            try{ bos.close(); } catch( Exception e ){}
1131
        }   // end finally
1132
        
1133
        return success;
1134
    }   // end encodeToFile
1135
    
1136
    
1137
    /**
1138
     * Convenience method for decoding data to a file.
1139
     *
1140
     * @param dataToDecode Base64-encoded data as a string
1141
     * @param filename Filename for saving decoded data
1142
     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
1143
     *
1144
     * @since 2.1
1145
     */
1146
    public static boolean decodeToFile( String dataToDecode, String filename )
1147
    {
1148
        boolean success = false;
1149
        Base64.OutputStream bos = null;
1150
        try
1151
        {
1152
                bos = new Base64.OutputStream( 
1153
                          new java.io.FileOutputStream( filename ), Base64.DECODE );
1154
                bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
1155
                success = true;
1156
        }   // end try
1157
        catch( java.io.IOException e )
1158
        {
1159
            success = false;
1160
        }   // end catch: IOException
1161
        finally
1162
        {
1163
                try{ bos.close(); } catch( Exception e ){}
1164
        }   // end finally
1165
        
1166
        return success;
1167
    }   // end decodeToFile
1168
    
1169
    
1170
    
1171
    
1172
    /**
1173
     * Convenience method for reading a base64-encoded
1174
     * file and decoding it.
1175
     *
1176
     * @param filename Filename for reading encoded data
1177
     * @return decoded byte array or null if unsuccessful
1178
     *
1179
     * @since 2.1
1180
     */
1181
    public static byte[] decodeFromFile( String filename )
1182
    {
1183
        byte[] decodedData = null;
1184
        Base64.InputStream bis = null;
1185
        try
1186
        {
1187
            // Set up some useful variables
1188
            java.io.File file = new java.io.File( filename );
1189
            byte[] buffer = null;
1190
            int length   = 0;
1191
            int numBytes = 0;
1192
            
1193
            // Check for size of file
1194
            if( file.length() > Integer.MAX_VALUE )
1195
            {
1196
                System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." );
1197
                return null;
1198
            }   // end if: file too big for int index
1199
            buffer = new byte[ (int)file.length() ];
1200
            
1201
            // Open a stream
1202
            bis = new Base64.InputStream( 
1203
                      new java.io.BufferedInputStream( 
1204
                      new java.io.FileInputStream( file ) ), Base64.DECODE );
1205
            
1206
            // Read until done
1207
            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
1208
                length += numBytes;
1209
            
1210
            // Save in a variable to return
1211
            decodedData = new byte[ length ];
1212
            System.arraycopy( buffer, 0, decodedData, 0, length );
1213
            
1214
        }   // end try
1215
        catch( java.io.IOException e )
1216
        {
1217
            System.err.println( "Error decoding from file " + filename );
1218
        }   // end catch: IOException
1219
        finally
1220
        {
1221
            try{ bis.close(); } catch( Exception e) {}
1222
        }   // end finally
1223
        
1224
        return decodedData;
1225
    }   // end decodeFromFile
1226
    
1227
    
1228
    
1229
    /**
1230
     * Convenience method for reading a binary file
1231
     * and base64-encoding it.
1232
     *
1233
     * @param filename Filename for reading binary data
1234
     * @return base64-encoded string or null if unsuccessful
1235
     *
1236
     * @since 2.1
1237
     */
1238
    public static String encodeFromFile( String filename )
1239
    {
1240
        String encodedData = null;
1241
        Base64.InputStream bis = null;
1242
        try
1243
        {
1244
            // Set up some useful variables
1245
            java.io.File file = new java.io.File( filename );
1246
            byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1)
1247
            int length   = 0;
1248
            int numBytes = 0;
1249
            
1250
            // Open a stream
1251
            bis = new Base64.InputStream( 
1252
                      new java.io.BufferedInputStream( 
1253
                      new java.io.FileInputStream( file ) ), Base64.ENCODE );
1254
            
1255
            // Read until done
1256
            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
1257
                length += numBytes;
1258
            
1259
            // Save in a variable to return
1260
            encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
1261
                
1262
        }   // end try
1263
        catch( java.io.IOException e )
1264
        {
1265
            System.err.println( "Error encoding from file " + filename );
1266
        }   // end catch: IOException
1267
        finally
1268
        {
1269
            try{ bis.close(); } catch( Exception e) {}
1270
        }   // end finally
1271
        
1272
        return encodedData;
1273
        }   // end encodeFromFile
1274
    
1275
    
1276
    
1277
    
1278
    /**
1279
     * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1280
     *
1281
     * @param infile Input file
1282
     * @param outfile Output file
1283
     * @return true if the operation is successful
1284
     * @since 2.2
1285
     */
1286
    public static boolean encodeFileToFile( String infile, String outfile )
1287
    {
1288
        boolean success = false;
1289
        java.io.InputStream in = null;
1290
        java.io.OutputStream out = null;
1291
        try{
1292
            in  = new Base64.InputStream( 
1293
                      new java.io.BufferedInputStream( 
1294
                      new java.io.FileInputStream( infile ) ), 
1295
                      Base64.ENCODE );
1296
            out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) );
1297
            byte[] buffer = new byte[65536]; // 64K
1298
            int read = -1;
1299
            while( ( read = in.read(buffer) ) >= 0 ){
1300
                out.write( buffer,0,read );
1301
            }   // end while: through file
1302
            success = true;
1303
        } catch( java.io.IOException exc ){
1304
            exc.printStackTrace();
1305
        } finally{
1306
            try{ in.close();  } catch( Exception exc ){}
1307
            try{ out.close(); } catch( Exception exc ){}
1308
        }   // end finally
1309
        
1310
        return success;
1311
    }   // end encodeFileToFile
1312
    
1313
    
1314
    
1315
    /**
1316
     * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1317
     *
1318
     * @param infile Input file
1319
     * @param outfile Output file
1320
     * @return true if the operation is successful
1321
     * @since 2.2
1322
     */
1323
    public static boolean decodeFileToFile( String infile, String outfile )
1324
    {
1325
        boolean success = false;
1326
        java.io.InputStream in = null;
1327
        java.io.OutputStream out = null;
1328
        try{
1329
            in  = new Base64.InputStream( 
1330
                      new java.io.BufferedInputStream( 
1331
                      new java.io.FileInputStream( infile ) ), 
1332
                      Base64.DECODE );
1333
            out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) );
1334
            byte[] buffer = new byte[65536]; // 64K
1335
            int read = -1;
1336
            while( ( read = in.read(buffer) ) >= 0 ){
1337
                out.write( buffer,0,read );
1338
            }   // end while: through file
1339
            success = true;
1340
        } catch( java.io.IOException exc ){
1341
            exc.printStackTrace();
1342
        } finally{
1343
            try{ in.close();  } catch( Exception exc ){}
1344
            try{ out.close(); } catch( Exception exc ){}
1345
        }   // end finally
1346
        
1347
        return success;
1348
    }   // end decodeFileToFile
1349
    
1350
    
1351
    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
1352
    
1353
    
1354
    
1355
    /**
1356
     * A {@link Base64.InputStream} will read data from another
1357
     * <tt>java.io.InputStream</tt>, given in the constructor,
1358
     * and encode/decode to/from Base64 notation on the fly.
1359
     *
1360
     * @see Base64
1361
     * @since 1.3
1362
     */
1363
    public static class InputStream extends java.io.FilterInputStream
1364
    {
1365
        private boolean encode;         // Encoding or decoding
1366
        private int     position;       // Current position in the buffer
1367
        private byte[]  buffer;         // Small buffer holding converted data
1368
        private int     bufferLength;   // Length of buffer (3 or 4)
1369
        private int     numSigBytes;    // Number of meaningful bytes in the buffer
1370
        private int     lineLength;
1371
        private boolean breakLines;     // Break lines at less than 80 characters
1372
                private int     options;        // Record options used to create the stream.
1373
                private byte[]  alphabet;            // Local copies to avoid extra method calls
1374
                private byte[]  decodabet;                // Local copies to avoid extra method calls
1375
        
1376
        
1377
        /**
1378
         * Constructs a {@link Base64.InputStream} in DECODE mode.
1379
         *
1380
         * @param in the <tt>java.io.InputStream</tt> from which to read data.
1381
         * @since 1.3
1382
         */
1383
        public InputStream( java.io.InputStream in )
1384
        {   
1385
            this( in, DECODE );
1386
        }   // end constructor
1387
        
1388
        
1389
        /**
1390
         * Constructs a {@link Base64.InputStream} in
1391
         * either ENCODE or DECODE mode.
1392
         * <p>
1393
         * Valid options:<pre>
1394
         *   ENCODE or DECODE: Encode or Decode as data is read.
1395
         *   DONT_BREAK_LINES: don't break lines at 76 characters
1396
         *     (only meaningful when encoding)
1397
         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
1398
         * </pre>
1399
         * <p>
1400
         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1401
         *
1402
         *
1403
         * @param in the <tt>java.io.InputStream</tt> from which to read data.
1404
         * @param options Specified options
1405
         * @see Base64#ENCODE
1406
         * @see Base64#DECODE
1407
         * @see Base64#DONT_BREAK_LINES
1408
         * @since 2.0
1409
         */
1410
        public InputStream( java.io.InputStream in, int options )
1411
        {   
1412
            super( in );
1413
            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1414
            this.encode       = (options & ENCODE) == ENCODE;
1415
            this.bufferLength = encode ? 4 : 3;
1416
            this.buffer       = new byte[ bufferLength ];
1417
            this.position     = -1;
1418
            this.lineLength   = 0;
1419
                        this.options      = options; // Record for later, mostly to determine which alphabet to use
1420
                        this.alphabet     = getAlphabet(options);
1421
                        this.decodabet    = getDecodabet(options);
1422
        }   // end constructor
1423
        
1424
        /**
1425
         * Reads enough of the input stream to convert
1426
         * to/from Base64 and returns the next byte.
1427
         *
1428
         * @return next byte
1429
         * @since 1.3
1430
         */
1431
        public int read() throws java.io.IOException 
1432
        { 
1433
            // Do we need to get data?
1434
            if( position < 0 )
1435
            {
1436
                if( encode )
1437
                {
1438
                    byte[] b3 = new byte[3];
1439
                    int numBinaryBytes = 0;
1440
                    for( int i = 0; i < 3; i++ )
1441
                    {
1442
                        try
1443
                        { 
1444
                            int b = in.read();
1445
                            
1446
                            // If end of stream, b is -1.
1447
                            if( b >= 0 )
1448
                            {
1449
                                b3[i] = (byte)b;
1450
                                numBinaryBytes++;
1451
                            }   // end if: not end of stream
1452
                            
1453
                        }   // end try: read
1454
                        catch( java.io.IOException e )
1455
                        {   
1456
                            // Only a problem if we got no data at all.
1457
                            if( i == 0 )
1458
                                throw e;
1459
                            
1460
                        }   // end catch
1461
                    }   // end for: each needed input byte
1462
                    
1463
                    if( numBinaryBytes > 0 )
1464
                    {
1465
                        encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
1466
                        position = 0;
1467
                        numSigBytes = 4;
1468
                    }   // end if: got data
1469
                    else
1470
                    {
1471
                        return -1;
1472
                    }   // end else
1473
                }   // end if: encoding
1474
                
1475
                // Else decoding
1476
                else
1477
                {
1478
                    byte[] b4 = new byte[4];
1479
                    int i = 0;
1480
                    for( i = 0; i < 4; i++ )
1481
                    {
1482
                        // Read four "meaningful" bytes:
1483
                        int b = 0;
1484
                        do{ b = in.read(); }
1485
                        while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
1486
                        
1487
                        if( b < 0 )
1488
                            break; // Reads a -1 if end of stream
1489
                        
1490
                        b4[i] = (byte)b;
1491
                    }   // end for: each needed input byte
1492
                    
1493
                    if( i == 4 )
1494
                    {
1495
                        numSigBytes = decode4to3( b4, 0, buffer, 0, options );
1496
                        position = 0;
1497
                    }   // end if: got four characters
1498
                    else if( i == 0 ){
1499
                        return -1;
1500
                    }   // end else if: also padded correctly
1501
                    else
1502
                    {
1503
                        // Must have broken out from above.
1504
                        throw new java.io.IOException( "Improperly padded Base64 input." );
1505
                    }   // end 
1506
                    
1507
                }   // end else: decode
1508
            }   // end else: get data
1509
            
1510
            // Got data?
1511
            if( position >= 0 )
1512
            {
1513
                // End of relevant data?
1514
                if( /*!encode &&*/ position >= numSigBytes )
1515
                    return -1;
1516
                
1517
                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
1518
                {
1519
                    lineLength = 0;
1520
                    return '\n';
1521
                }   // end if
1522
                else
1523
                {
1524
                    lineLength++;   // This isn't important when decoding
1525
                                    // but throwing an extra "if" seems
1526
                                    // just as wasteful.
1527
                    
1528
                    int b = buffer[ position++ ];
1529

    
1530
                    if( position >= bufferLength )
1531
                        position = -1;
1532

    
1533
                    return b & 0xFF; // This is how you "cast" a byte that's
1534
                                     // intended to be unsigned.
1535
                }   // end else
1536
            }   // end if: position >= 0
1537
            
1538
            // Else error
1539
            else
1540
            {   
1541
                // When JDK1.4 is more accepted, use an assertion here.
1542
                throw new java.io.IOException( "Error in Base64 code reading stream." );
1543
            }   // end else
1544
        }   // end read
1545
        
1546
        
1547
        /**
1548
         * Calls {@link #read()} repeatedly until the end of stream
1549
         * is reached or <var>len</var> bytes are read.
1550
         * Returns number of bytes read into array or -1 if
1551
         * end of stream is encountered.
1552
         *
1553
         * @param dest array to hold values
1554
         * @param off offset for array
1555
         * @param len max number of bytes to read into array
1556
         * @return bytes read into array or -1 if end of stream is encountered.
1557
         * @since 1.3
1558
         */
1559
        public int read( byte[] dest, int off, int len ) throws java.io.IOException
1560
        {
1561
            int i;
1562
            int b;
1563
            for( i = 0; i < len; i++ )
1564
            {
1565
                b = read();
1566
                
1567
                //if( b < 0 && i == 0 )
1568
                //    return -1;
1569
                
1570
                if( b >= 0 )
1571
                    dest[off + i] = (byte)b;
1572
                else if( i == 0 )
1573
                    return -1;
1574
                else
1575
                    break; // Out of 'for' loop
1576
            }   // end for: each byte read
1577
            return i;
1578
        }   // end read
1579
        
1580
    }   // end inner class InputStream
1581
    
1582
    
1583
    
1584
    
1585
    
1586
    
1587
    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
1588
    
1589
    
1590
    
1591
    /**
1592
     * A {@link Base64.OutputStream} will write data to another
1593
     * <tt>java.io.OutputStream</tt>, given in the constructor,
1594
     * and encode/decode to/from Base64 notation on the fly.
1595
     *
1596
     * @see Base64
1597
     * @since 1.3
1598
     */
1599
    public static class OutputStream extends java.io.FilterOutputStream
1600
    {
1601
        private boolean encode;
1602
        private int     position;
1603
        private byte[]  buffer;
1604
        private int     bufferLength;
1605
        private int     lineLength;
1606
        private boolean breakLines;
1607
        private byte[]  b4; // Scratch used in a few places
1608
        private boolean suspendEncoding;
1609
                private int options; // Record for later
1610
                private byte[]  alphabet;            // Local copies to avoid extra method calls
1611
                private byte[]  decodabet;                // Local copies to avoid extra method calls
1612
        
1613
        /**
1614
         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1615
         *
1616
         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1617
         * @since 1.3
1618
         */
1619
        public OutputStream( java.io.OutputStream out )
1620
        {   
1621
            this( out, ENCODE );
1622
        }   // end constructor
1623
        
1624
        
1625
        /**
1626
         * Constructs a {@link Base64.OutputStream} in
1627
         * either ENCODE or DECODE mode.
1628
         * <p>
1629
         * Valid options:<pre>
1630
         *   ENCODE or DECODE: Encode or Decode as data is read.
1631
         *   DONT_BREAK_LINES: don't break lines at 76 characters
1632
         *     (only meaningful when encoding)
1633
         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
1634
         * </pre>
1635
         * <p>
1636
         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1637
         *
1638
         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1639
         * @param options Specified options.
1640
         * @see Base64#ENCODE
1641
         * @see Base64#DECODE
1642
         * @see Base64#DONT_BREAK_LINES
1643
         * @since 1.3
1644
         */
1645
        public OutputStream( java.io.OutputStream out, int options )
1646
        {   
1647
            super( out );
1648
            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1649
            this.encode       = (options & ENCODE) == ENCODE;
1650
            this.bufferLength = encode ? 3 : 4;
1651
            this.buffer       = new byte[ bufferLength ];
1652
            this.position     = 0;
1653
            this.lineLength   = 0;
1654
            this.suspendEncoding = false;
1655
            this.b4           = new byte[4];
1656
                        this.options      = options;
1657
                        this.alphabet     = getAlphabet(options);
1658
                        this.decodabet    = getDecodabet(options);
1659
        }   // end constructor
1660
        
1661
        
1662
        /**
1663
         * Writes the byte to the output stream after
1664
         * converting to/from Base64 notation.
1665
         * When encoding, bytes are buffered three
1666
         * at a time before the output stream actually
1667
         * gets a write() call.
1668
         * When decoding, bytes are buffered four
1669
         * at a time.
1670
         *
1671
         * @param theByte the byte to write
1672
         * @since 1.3
1673
         */
1674
        public void write(int theByte) throws java.io.IOException
1675
        {
1676
            // Encoding suspended?
1677
            if( suspendEncoding )
1678
            {
1679
                super.out.write( theByte );
1680
                return;
1681
            }   // end if: supsended
1682
            
1683
            // Encode?
1684
            if( encode )
1685
            {
1686
                buffer[ position++ ] = (byte)theByte;
1687
                if( position >= bufferLength )  // Enough to encode.
1688
                {
1689
                    out.write( encode3to4( b4, buffer, bufferLength, options ) );
1690

    
1691
                    lineLength += 4;
1692
                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
1693
                    {
1694
                        out.write( NEW_LINE );
1695
                        lineLength = 0;
1696
                    }   // end if: end of line
1697

    
1698
                    position = 0;
1699
                }   // end if: enough to output
1700
            }   // end if: encoding
1701

    
1702
            // Else, Decoding
1703
            else
1704
            {
1705
                // Meaningful Base64 character?
1706
                if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1707
                {
1708
                    buffer[ position++ ] = (byte)theByte;
1709
                    if( position >= bufferLength )  // Enough to output.
1710
                    {
1711
                        int len = Base64.decode4to3( buffer, 0, b4, 0, options );
1712
                        out.write( b4, 0, len );
1713
                        //out.write( Base64.decode4to3( buffer ) );
1714
                        position = 0;
1715
                    }   // end if: enough to output
1716
                }   // end if: meaningful base64 character
1717
                else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1718
                {
1719
                    throw new java.io.IOException( "Invalid character in Base64 data." );
1720
                }   // end else: not white space either
1721
            }   // end else: decoding
1722
        }   // end write
1723
        
1724
        
1725
        
1726
        /**
1727
         * Calls {@link #write(int)} repeatedly until <var>len</var> 
1728
         * bytes are written.
1729
         *
1730
         * @param theBytes array from which to read bytes
1731
         * @param off offset for array
1732
         * @param len max number of bytes to read into array
1733
         * @since 1.3
1734
         */
1735
        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
1736
        {
1737
            // Encoding suspended?
1738
            if( suspendEncoding )
1739
            {
1740
                super.out.write( theBytes, off, len );
1741
                return;
1742
            }   // end if: supsended
1743
            
1744
            for( int i = 0; i < len; i++ )
1745
            {
1746
                write( theBytes[ off + i ] );
1747
            }   // end for: each byte written
1748
            
1749
        }   // end write
1750
        
1751
        
1752
        
1753
        /**
1754
         * Method added by PHIL. [Thanks, PHIL. -Rob]
1755
         * This pads the buffer without closing the stream.
1756
         */
1757
        public void flushBase64() throws java.io.IOException 
1758
        {
1759
            if( position > 0 )
1760
            {
1761
                if( encode )
1762
                {
1763
                    out.write( encode3to4( b4, buffer, position, options ) );
1764
                    position = 0;
1765
                }   // end if: encoding
1766
                else
1767
                {
1768
                    throw new java.io.IOException( "Base64 input not properly padded." );
1769
                }   // end else: decoding
1770
            }   // end if: buffer partially full
1771

    
1772
        }   // end flush
1773

    
1774
        
1775
        /** 
1776
         * Flushes and closes (I think, in the superclass) the stream. 
1777
         *
1778
         * @since 1.3
1779
         */
1780
        public void close() throws java.io.IOException
1781
        {
1782
            // 1. Ensure that pending characters are written
1783
            flushBase64();
1784

    
1785
            // 2. Actually close the stream
1786
            // Base class both flushes and closes.
1787
            super.close();
1788
            
1789
            buffer = null;
1790
            out    = null;
1791
        }   // end close
1792
        
1793
        
1794
        
1795
        /**
1796
         * Suspends encoding of the stream.
1797
         * May be helpful if you need to embed a piece of
1798
         * base640-encoded data in a stream.
1799
         *
1800
         * @since 1.5.1
1801
         */
1802
        public void suspendEncoding() throws java.io.IOException 
1803
        {
1804
            flushBase64();
1805
            this.suspendEncoding = true;
1806
        }   // end suspendEncoding
1807
        
1808
        
1809
        /**
1810
         * Resumes encoding of the stream.
1811
         * May be helpful if you need to embed a piece of
1812
         * base640-encoded data in a stream.
1813
         *
1814
         * @since 1.5.1
1815
         */
1816
        public void resumeEncoding()
1817
        {
1818
            this.suspendEncoding = false;
1819
        }   // end resumeEncoding
1820
        
1821
        
1822
        
1823
    }   // end inner class OutputStream
1824
    
1825
    
1826
}   // end class Base64