KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > util > Base64


1 package org.codehaus.aspectwerkz.util;
2
3 /**
4  * Encodes and decodes to and from Base64 notation.
5  * <p/>
6  * <p/>
7  * Change Log:
8  * </p>
9  * <ul>
10  * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
11  * some convenience methods for reading and writing to and from files.</li>
12  * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
13  * with other encodings (like EBCDIC).</li>
14  * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
15  * encoded data was a single byte.</li>
16  * <li>v2.0 - I got rid of methods that used booleans to set options.
17  * Now everything is more consolidated and cleaner. The code now detects
18  * when data that's being decoded is gzip-compressed and will decompress it
19  * automatically. Generally things are cleaner. You'll probably have to
20  * change some method calls that you were making to support the new
21  * options format (<tt>int</tt>s that you "OR" together).</li>
22  * <li>v1.5.2 - Fixed bug when decompressing and decoding to a
23  * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
24  * Added the ability to "suspend" encoding in the Output Stream so
25  * you can turn on and off the encoding if you need to embed base64
26  * data in an otherwise "normal" stream (like an XML file).</li>
27  * <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
28  * This helps when using GZIP streams.
29  * Added the ability to GZip-compress objects before encoding them.</li>
30  * <li>v1.4 - Added helper methods to read/write files.</li>
31  * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
32  * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
33  * where last buffer being read, if not completely full, was not returned.</li>
34  * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
35  * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
36  * </ul>
37  * <p/>
38  * <p/>
39  * I am placing this code in the Public Domain. Do with it as you will.
40  * This software comes with no guarantees or warranties but with
41  * plenty of well-wishing instead!
42  * Please visit <a HREF="http://iharder.net/base64">http://iharder.net/base64</a>
43  * periodically to check for updates or to contribute improvements.
44  * </p>
45  *
46  * @author Robert Harder
47  * @author rob@iharder.net
48  * @version 2.1
49  */

50 public class Base64 {
51
52 /* ******** P U B L I C F I E L D S ******** */
53
54
55     /**
56      * No options specified. Value is zero.
57      */

58     public final static int NO_OPTIONS = 0;
59
60     /**
61      * Specify encoding.
62      */

63     public final static int ENCODE = 1;
64
65
66     /**
67      * Specify decoding.
68      */

69     public final static int DECODE = 0;
70
71
72     /**
73      * Specify that data should be gzip-compressed.
74      */

75     public final static int GZIP = 2;
76
77
78     /**
79      * Don't break lines when encoding (violates strict Base64 specification)
80      */

81     public final static int DONT_BREAK_LINES = 8;
82
83
84 /* ******** P R I V A T E F I E L D S ******** */
85
86
87     /**
88      * Maximum line length (76) of Base64 output.
89      */

90     private final static int MAX_LINE_LENGTH = 76;
91
92
93     /**
94      * The equals sign (=) as a byte.
95      */

96     private final static byte EQUALS_SIGN = (byte) '=';
97
98
99     /**
100      * The new line character (\n) as a byte.
101      */

102     private final static byte NEW_LINE = (byte) '\n';
103
104
105     /**
106      * Preferred encoding.
107      */

108     private final static String JavaDoc PREFERRED_ENCODING = "UTF-8";
109
110
111     /**
112      * The 64 valid Base64 values.
113      */

114     private final static byte[] ALPHABET;
115     private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */
116             {
117                 (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
118                 (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
119                 (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
120                 (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
121                 (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g',
122                 (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
123                 (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
124                 (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
125                 (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
126                 (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/'
127             };
128
129     /** Determine which ALPHABET to use. */
130     static {
131         byte[] __bytes;
132         try {
133             __bytes =
134             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes(PREFERRED_ENCODING);
135         } // end try
136
catch (java.io.UnsupportedEncodingException JavaDoc use) {
137             __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
138
} // end catch
139
ALPHABET = __bytes;
140     } // end static
141

142
143     /**
144      * Translates a Base64 value to either its 6-bit reconstruction value
145      * or a negative number indicating some other meaning.
146      */

147     private final static byte[] DECODABET =
148             {
149                 -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
150
-5, -5, // Whitespace: Tab and Linefeed
151
-9, -9, // Decimal 11 - 12
152
-5, // Whitespace: Carriage Return
153
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
154
-9, -9, -9, -9, -9, // Decimal 27 - 31
155
-5, // Whitespace: Space
156
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
157
62, // Plus sign at decimal 43
158
-9, -9, -9, // Decimal 44 - 46
159
63, // Slash at decimal 47
160
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
161
-9, -9, -9, // Decimal 58 - 60
162
-1, // Equals sign at decimal 61
163
-9, -9, -9, // Decimal 62 - 64
164
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
165
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
166
-9, -9, -9, -9, -9, -9, // Decimal 91 - 96
167
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
168
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
169
-9, -9, -9, -9 // Decimal 123 - 126
170
/*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
171                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
172                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
173                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
174                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
175                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
176                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
177                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
178                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
179                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */

180             };
181
182     // I think I end up not using the BAD_ENCODING indicator.
183
//private final static byte BAD_ENCODING = -9; // Indicates error in encoding
184
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
185
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
186

187
188     /**
189      * Defeats instantiation.
190      */

191     private Base64() {
192     }
193
194
195
196 /* ******** E N C O D I N G M E T H O D S ******** */
197
198
199     /**
200      * Encodes up to the first three bytes of array <var>threeBytes</var>
201      * and returns a four-byte array in Base64 notation.
202      * The actual number of significant bytes in your array is
203      * given by <var>numSigBytes</var>.
204      * The array <var>threeBytes</var> needs only be as big as
205      * <var>numSigBytes</var>.
206      * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
207      *
208      * @param b4 A reusable byte array to reduce array instantiation
209      * @param threeBytes the array to convert
210      * @param numSigBytes the number of significant bytes in your array
211      * @return four byte array in Base64 notation.
212      * @since 1.5.2
213      */

214     private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes) {
215         encode3to4(threeBytes, 0, numSigBytes, b4, 0);
216         return b4;
217     } // end encode3to4
218

219
220     /**
221      * Encodes up to three bytes of the array <var>source</var>
222      * and writes the resulting four Base64 bytes to <var>destination</var>.
223      * The source and destination arrays can be manipulated
224      * anywhere along their length by specifying
225      * <var>srcOffset</var> and <var>destOffset</var>.
226      * This method does not check to make sure your arrays
227      * are large enough to accomodate <var>srcOffset</var> + 3 for
228      * the <var>source</var> array or <var>destOffset</var> + 4 for
229      * the <var>destination</var> array.
230      * The actual number of significant bytes in your array is
231      * given by <var>numSigBytes</var>.
232      *
233      * @param source the array to convert
234      * @param srcOffset the index where conversion begins
235      * @param numSigBytes the number of significant bytes in your array
236      * @param destination the array to hold the conversion
237      * @param destOffset the index where output will be put
238      * @return the <var>destination</var> array
239      * @since 1.3
240      */

241     private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes,
242                                      byte[] destination, int destOffset) {
243         // 1 2 3
244
// 01234567890123456789012345678901 Bit position
245
// --------000000001111111122222222 Array position from threeBytes
246
// --------| || || || | Six bit groups to index ALPHABET
247
// >>18 >>12 >> 6 >> 0 Right shift necessary
248
// 0x3f 0x3f 0x3f Additional AND
249

250         // Create buffer with zero-padding if there are only one or two
251
// significant bytes passed in the array.
252
// We have to shift left 24 in order to flush out the 1's that appear
253
// when Java treats a value as negative that is cast from a byte to an int.
254
int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
255                      | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
256                      | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
257
258         switch (numSigBytes) {
259             case 3:
260                 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
261                 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
262                 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
263                 destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
264                 return destination;
265
266             case 2:
267                 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
268                 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
269                 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
270                 destination[destOffset + 3] = EQUALS_SIGN;
271                 return destination;
272
273             case 1:
274                 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
275                 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
276                 destination[destOffset + 2] = EQUALS_SIGN;
277                 destination[destOffset + 3] = EQUALS_SIGN;
278                 return destination;
279
280             default:
281                 return destination;
282         } // end switch
283
} // end encode3to4
284

285
286     /**
287      * Serializes an object and returns the Base64-encoded
288      * version of that serialized object. If the object
289      * cannot be serialized or there is another error,
290      * the method will return <tt>null</tt>.
291      * The object is not GZip-compressed before being encoded.
292      *
293      * @param serializableObject The object to encode
294      * @return The Base64-encoded object
295      * @since 1.4
296      */

297     public static String JavaDoc encodeObject(java.io.Serializable JavaDoc serializableObject) {
298         return encodeObject(serializableObject, NO_OPTIONS);
299     } // end encodeObject
300

301
302     /**
303      * Serializes an object and returns the Base64-encoded
304      * version of that serialized object. If the object
305      * cannot be serialized or there is another error,
306      * the method will return <tt>null</tt>.
307      * <p/>
308      * Valid options:<pre>
309      * GZIP: gzip-compresses object before encoding it.
310      * DONT_BREAK_LINES: don't break lines at 76 characters
311      * <i>Note: Technically, this makes your encoding non-compliant.</i>
312      * </pre>
313      * <p/>
314      * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
315      * <p/>
316      * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
317      *
318      * @param serializableObject The object to encode
319      * @param options Specified options
320      * @return The Base64-encoded object
321      * @see Base64#GZIP
322      * @see Base64#DONT_BREAK_LINES
323      * @since 2.0
324      */

325     public static String JavaDoc encodeObject(java.io.Serializable JavaDoc serializableObject, int options) {
326         // Streams
327
java.io.ByteArrayOutputStream JavaDoc baos = null;
328         java.io.OutputStream JavaDoc b64os = null;
329         java.io.ObjectOutputStream JavaDoc oos = null;
330         java.util.zip.GZIPOutputStream JavaDoc gzos = null;
331
332         // Isolate options
333
int gzip = (options & GZIP);
334         int dontBreakLines = (options & DONT_BREAK_LINES);
335
336         try {
337             // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
338
baos = new java.io.ByteArrayOutputStream JavaDoc();
339             b64os = new Base64.OutputStream(baos, ENCODE | dontBreakLines);
340
341             // GZip?
342
if (gzip == GZIP) {
343                 gzos = new java.util.zip.GZIPOutputStream JavaDoc(b64os);
344                 oos = new java.io.ObjectOutputStream JavaDoc(gzos);
345             } // end if: gzip
346
else {
347                 oos = new java.io.ObjectOutputStream JavaDoc(b64os);
348             }
349
350             oos.writeObject(serializableObject);
351         } // end try
352
catch (java.io.IOException JavaDoc e) {
353             e.printStackTrace();
354             return null;
355         } // end catch
356
finally {
357             try {
358                 oos.close();
359             } catch (Exception JavaDoc e) {
360             }
361             try {
362                 gzos.close();
363             } catch (Exception JavaDoc e) {
364             }
365             try {
366                 b64os.close();
367             } catch (Exception JavaDoc e) {
368             }
369             try {
370                 baos.close();
371             } catch (Exception JavaDoc e) {
372             }
373         } // end finally
374

375         // Return value according to relevant encoding.
376
try {
377             return new String JavaDoc(baos.toByteArray(), PREFERRED_ENCODING);
378         } // end try
379
catch (java.io.UnsupportedEncodingException JavaDoc uue) {
380             return new String JavaDoc(baos.toByteArray());
381         } // end catch
382

383     } // end encode
384

385
386     /**
387      * Encodes a byte array into Base64 notation.
388      * Does not GZip-compress data.
389      *
390      * @param source The data to convert
391      * @since 1.4
392      */

393     public static String JavaDoc encodeBytes(byte[] source) {
394         return encodeBytes(source, 0, source.length, NO_OPTIONS);
395     } // end encodeBytes
396

397
398     /**
399      * Encodes a byte array into Base64 notation.
400      * <p/>
401      * Valid options:<pre>
402      * GZIP: gzip-compresses object before encoding it.
403      * DONT_BREAK_LINES: don't break lines at 76 characters
404      * <i>Note: Technically, this makes your encoding non-compliant.</i>
405      * </pre>
406      * <p/>
407      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
408      * <p/>
409      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
410      *
411      * @param source The data to convert
412      * @param options Specified options
413      * @see Base64#GZIP
414      * @see Base64#DONT_BREAK_LINES
415      * @since 2.0
416      */

417     public static String JavaDoc encodeBytes(byte[] source, int options) {
418         return encodeBytes(source, 0, source.length, options);
419     } // end encodeBytes
420

421
422     /**
423      * Encodes a byte array into Base64 notation.
424      * Does not GZip-compress data.
425      *
426      * @param source The data to convert
427      * @param off Offset in array where conversion should begin
428      * @param len Length of data to convert
429      * @since 1.4
430      */

431     public static String JavaDoc encodeBytes(byte[] source, int off, int len) {
432         return encodeBytes(source, off, len, NO_OPTIONS);
433     } // end encodeBytes
434

435
436     /**
437      * Encodes a byte array into Base64 notation.
438      * <p/>
439      * Valid options:<pre>
440      * GZIP: gzip-compresses object before encoding it.
441      * DONT_BREAK_LINES: don't break lines at 76 characters
442      * <i>Note: Technically, this makes your encoding non-compliant.</i>
443      * </pre>
444      * <p/>
445      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
446      * <p/>
447      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
448      *
449      * @param source The data to convert
450      * @param off Offset in array where conversion should begin
451      * @param len Length of data to convert
452      * @param options Specified options
453      * @see Base64#GZIP
454      * @see Base64#DONT_BREAK_LINES
455      * @since 2.0
456      */

457     public static String JavaDoc encodeBytes(byte[] source, int off, int len, int options) {
458         // Isolate options
459
int dontBreakLines = (options & DONT_BREAK_LINES);
460         int gzip = (options & GZIP);
461
462         // Compress?
463
if (gzip == GZIP) {
464             java.io.ByteArrayOutputStream JavaDoc baos = null;
465             java.util.zip.GZIPOutputStream JavaDoc gzos = null;
466             Base64.OutputStream b64os = null;
467
468
469             try {
470                 // GZip -> Base64 -> ByteArray
471
baos = new java.io.ByteArrayOutputStream JavaDoc();
472                 b64os = new Base64.OutputStream(baos, ENCODE | dontBreakLines);
473                 gzos = new java.util.zip.GZIPOutputStream JavaDoc(b64os);
474
475                 gzos.write(source, off, len);
476                 gzos.close();
477             } // end try
478
catch (java.io.IOException JavaDoc e) {
479                 e.printStackTrace();
480                 return null;
481             } // end catch
482
finally {
483                 try {
484                     gzos.close();
485                 } catch (Exception JavaDoc e) {
486                 }
487                 try {
488                     b64os.close();
489                 } catch (Exception JavaDoc e) {
490                 }
491                 try {
492                     baos.close();
493                 } catch (Exception JavaDoc e) {
494                 }
495             } // end finally
496

497             // Return value according to relevant encoding.
498
try {
499                 return new String JavaDoc(baos.toByteArray(), PREFERRED_ENCODING);
500             } // end try
501
catch (java.io.UnsupportedEncodingException JavaDoc uue) {
502                 return new String JavaDoc(baos.toByteArray());
503             } // end catch
504
} // end if: compress
505

506         // Else, don't compress. Better not to use streams at all then.
507
else {
508             // Convert option to boolean in way that code likes it.
509
boolean breakLines = dontBreakLines == 0;
510
511             int len43 = len * 4 / 3;
512             byte[] outBuff = new byte[(len43) // Main 4:3
513
+ ((len % 3) > 0 ? 4 : 0) // Account for padding
514
+ (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
515
int d = 0;
516             int e = 0;
517             int len2 = len - 2;
518             int lineLength = 0;
519             for (; d < len2; d += 3, e += 4) {
520                 encode3to4(source, d + off, 3, outBuff, e);
521
522                 lineLength += 4;
523                 if (breakLines && lineLength == MAX_LINE_LENGTH) {
524                     outBuff[e + 4] = NEW_LINE;
525                     e++;
526                     lineLength = 0;
527                 } // end if: end of line
528
} // en dfor: each piece of array
529

530             if (d < len) {
531                 encode3to4(source, d + off, len - d, outBuff, e);
532                 e += 4;
533             } // end if: some padding needed
534

535
536             // Return value according to relevant encoding.
537
try {
538                 return new String JavaDoc(outBuff, 0, e, PREFERRED_ENCODING);
539             } // end try
540
catch (java.io.UnsupportedEncodingException JavaDoc uue) {
541                 return new String JavaDoc(outBuff, 0, e);
542             } // end catch
543

544         } // end else: don't compress
545

546     } // end encodeBytes
547

548
549
550
551
552 /* ******** D E C O D I N G M E T H O D S ******** */
553
554
555     /**
556      * Decodes four bytes from array <var>source</var>
557      * and writes the resulting bytes (up to three of them)
558      * to <var>destination</var>.
559      * The source and destination arrays can be manipulated
560      * anywhere along their length by specifying
561      * <var>srcOffset</var> and <var>destOffset</var>.
562      * This method does not check to make sure your arrays
563      * are large enough to accomodate <var>srcOffset</var> + 4 for
564      * the <var>source</var> array or <var>destOffset</var> + 3 for
565      * the <var>destination</var> array.
566      * This method returns the actual number of bytes that
567      * were converted from the Base64 encoding.
568      *
569      * @param source the array to convert
570      * @param srcOffset the index where conversion begins
571      * @param destination the array to hold the conversion
572      * @param destOffset the index where output will be put
573      * @return the number of decoded bytes converted
574      * @since 1.3
575      */

576     private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) {
577         // Example: Dk==
578
if (source[srcOffset + 2] == EQUALS_SIGN) {
579             // Two ways to do the same thing. Don't know which way I like best.
580
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
581
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
582
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
583                           | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
584
585             destination[destOffset] = (byte) (outBuff >>> 16);
586             return 1;
587         }
588
589         // Example: DkL=
590
else if (source[srcOffset + 3] == EQUALS_SIGN) {
591             // Two ways to do the same thing. Don't know which way I like best.
592
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
593
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
594
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
595
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
596                           | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
597                           | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
598
599             destination[destOffset] = (byte) (outBuff >>> 16);
600             destination[destOffset + 1] = (byte) (outBuff >>> 8);
601             return 2;
602         }
603
604         // Example: DkLE
605
else {
606             try {
607                 // Two ways to do the same thing. Don't know which way I like best.
608
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
609
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
610
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
611
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
612
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
613                               | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
614                               | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
615                               | ((DECODABET[source[srcOffset + 3]] & 0xFF));
616
617
618                 destination[destOffset] = (byte) (outBuff >> 16);
619                 destination[destOffset + 1] = (byte) (outBuff >> 8);
620                 destination[destOffset + 2] = (byte) (outBuff);
621
622                 return 3;
623             } catch (Exception JavaDoc e) {
624                 System.out.println("" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]]));
625                 System.out.println("" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]]));
626                 System.out.println("" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]));
627                 System.out.println("" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]]));
628                 return -1;
629             } //e nd catch
630
}
631     } // end decodeToBytes
632

633
634     /**
635      * Very low-level access to decoding ASCII characters in
636      * the form of a byte array. Does not support automatically
637      * gunzipping or any other "fancy" features.
638      *
639      * @param source The Base64 encoded data
640      * @param off The offset of where to begin decoding
641      * @param len The length of characters to decode
642      * @return decoded data
643      * @since 1.3
644      */

645     public static byte[] decode(byte[] source, int off, int len) {
646         int len34 = len * 3 / 4;
647         byte[] outBuff = new byte[len34]; // Upper limit on size of output
648
int outBuffPosn = 0;
649
650         byte[] b4 = new byte[4];
651         int b4Posn = 0;
652         int i = 0;
653         byte sbiCrop = 0;
654         byte sbiDecode = 0;
655         for (i = off; i < off + len; i++) {
656             sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
657
sbiDecode = DECODABET[sbiCrop];
658
659             if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better
660
{
661                 if (sbiDecode >= EQUALS_SIGN_ENC) {
662                     b4[b4Posn++] = sbiCrop;
663                     if (b4Posn > 3) {
664                         outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
665                         b4Posn = 0;
666
667                         // If that was the equals sign, break out of 'for' loop
668
if (sbiCrop == EQUALS_SIGN) {
669                             break;
670                         }
671                     } // end if: quartet built
672

673                 } // end if: equals sign or better
674

675             } // end if: white space, equals sign or better
676
else {
677                 System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
678                 return null;
679             } // end else:
680
} // each input character
681

682         byte[] out = new byte[outBuffPosn];
683         System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
684         return out;
685     } // end decode
686

687
688     /**
689      * Decodes data from Base64 notation, automatically
690      * detecting gzip-compressed data and decompressing it.
691      *
692      * @param s the string to decode
693      * @return the decoded data
694      * @since 1.4
695      */

696     public static byte[] decode(String JavaDoc s) {
697         byte[] bytes;
698         try {
699             bytes = s.getBytes(PREFERRED_ENCODING);
700         } // end try
701
catch (java.io.UnsupportedEncodingException JavaDoc uee) {
702             bytes = s.getBytes();
703         } // end catch
704
//</change>
705

706         // Decode
707
bytes = decode(bytes, 0, bytes.length);
708
709
710         // Check to see if it's gzip-compressed
711
// GZIP Magic Two-Byte Number: 0x8b1f (35615)
712
if (bytes != null && bytes.length >= 4) {
713
714             int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
715             if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
716                 java.io.ByteArrayInputStream JavaDoc bais = null;
717                 java.util.zip.GZIPInputStream JavaDoc gzis = null;
718                 java.io.ByteArrayOutputStream JavaDoc baos = null;
719                 byte[] buffer = new byte[2048];
720                 int length = 0;
721
722                 try {
723                     baos = new java.io.ByteArrayOutputStream JavaDoc();
724                     bais = new java.io.ByteArrayInputStream JavaDoc(bytes);
725                     gzis = new java.util.zip.GZIPInputStream JavaDoc(bais);
726
727                     while ((length = gzis.read(buffer)) >= 0) {
728                         baos.write(buffer, 0, length);
729                     } // end while: reading input
730

731                     // No error? Get new bytes.
732
bytes = baos.toByteArray();
733
734                 } // end try
735
catch (java.io.IOException JavaDoc e) {
736                     // Just return originally-decoded bytes
737
} // end catch
738
finally {
739                     try {
740                         baos.close();
741                     } catch (Exception JavaDoc e) {
742                     }
743                     try {
744                         gzis.close();
745                     } catch (Exception JavaDoc e) {
746                     }
747                     try {
748                         bais.close();
749                     } catch (Exception JavaDoc e) {
750                     }
751                 } // end finally
752

753             } // end if: gzipped
754
} // end if: bytes.length >= 2
755

756         return bytes;
757     } // end decode
758

759
760     /**
761      * Attempts to decode Base64 data and deserialize a Java
762      * Object within. Returns <tt>null</tt> if there was an error.
763      *
764      * @param encodedObject The Base64 data to decode
765      * @return The decoded and deserialized object
766      * @since 1.5
767      */

768     public static Object JavaDoc decodeToObject(String JavaDoc encodedObject) {
769         // Decode and gunzip if necessary
770
byte[] objBytes = decode(encodedObject);
771
772         java.io.ByteArrayInputStream JavaDoc bais = null;
773         java.io.ObjectInputStream JavaDoc ois = null;
774         Object JavaDoc obj = null;
775
776         try {
777             bais = new java.io.ByteArrayInputStream JavaDoc(objBytes);
778             ois = new java.io.ObjectInputStream JavaDoc(bais);
779
780             obj = ois.readObject();
781         } // end try
782
catch (java.io.IOException JavaDoc e) {
783             e.printStackTrace();
784             obj = null;
785         } // end catch
786
catch (java.lang.ClassNotFoundException JavaDoc e) {
787             e.printStackTrace();
788             obj = null;
789         } // end catch
790
finally {
791             try {
792                 bais.close();
793             } catch (Exception JavaDoc e) {
794             }
795             try {
796                 ois.close();
797             } catch (Exception JavaDoc e) {
798             }
799         } // end finally
800

801         return obj;
802     } // end decodeObject
803

804
805     /**
806      * Convenience method for encoding data to a file.
807      *
808      * @param dataToEncode byte array of data to encode in base64 form
809      * @param filename Filename for saving encoded data
810      * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
811      * @since 2.1
812      */

813     public static boolean encodeToFile(byte[] dataToEncode, String JavaDoc filename) {
814         boolean success = false;
815         Base64.OutputStream bos = null;
816         try {
817             bos = new Base64.OutputStream(new java.io.FileOutputStream JavaDoc(filename), Base64.ENCODE);
818             bos.write(dataToEncode);
819             success = true;
820         } // end try
821
catch (java.io.IOException JavaDoc e) {
822
823             success = false;
824         } // end catch: IOException
825
finally {
826             try {
827                 bos.close();
828             } catch (Exception JavaDoc e) {
829             }
830         } // end finally
831

832         return success;
833     } // end encodeToFile
834

835
836     /**
837      * Convenience method for decoding data to a file.
838      *
839      * @param dataToDecode Base64-encoded data as a string
840      * @param filename Filename for saving decoded data
841      * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
842      * @since 2.1
843      */

844     public static boolean decodeToFile(String JavaDoc dataToDecode, String JavaDoc filename) {
845         boolean success = false;
846         Base64.OutputStream bos = null;
847         try {
848             bos = new Base64.OutputStream(new java.io.FileOutputStream JavaDoc(filename), Base64.DECODE);
849             bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
850             success = true;
851         } // end try
852
catch (java.io.IOException JavaDoc e) {
853             success = false;
854         } // end catch: IOException
855
finally {
856             try {
857                 bos.close();
858             } catch (Exception JavaDoc e) {
859             }
860         } // end finally
861

862         return success;
863     } // end decodeToFile
864

865
866     /**
867      * Convenience method for reading a base64-encoded
868      * file and decoding it.
869      *
870      * @param filename Filename for reading encoded data
871      * @return decoded byte array or null if unsuccessful
872      * @since 2.1
873      */

874     public static byte[] decodeFromFile(String JavaDoc filename) {
875         byte[] decodedData = null;
876         Base64.InputStream bis = null;
877         try {
878             // Set up some useful variables
879
java.io.File JavaDoc file = new java.io.File JavaDoc(filename);
880             byte[] buffer = null;
881             int length = 0;
882             int numBytes = 0;
883
884             // Check for size of file
885
if (file.length() > Integer.MAX_VALUE) {
886                 System.err.println("File is too big for this convenience method (" + file.length() + " bytes).");
887                 return null;
888             } // end if: file too big for int index
889
buffer = new byte[(int) file.length()];
890
891             // Open a stream
892
bis = new Base64.InputStream(
893                     new java.io.BufferedInputStream JavaDoc(new java.io.FileInputStream JavaDoc(file)), Base64.DECODE
894             );
895
896             // Read until done
897
while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
898                 length += numBytes;
899             }
900
901             // Save in a variable to return
902
decodedData = new byte[length];
903             System.arraycopy(buffer, 0, decodedData, 0, length);
904
905         } // end try
906
catch (java.io.IOException JavaDoc e) {
907             System.err.println("Error decoding from file " + filename);
908         } // end catch: IOException
909
finally {
910             try {
911                 bis.close();
912             } catch (Exception JavaDoc e) {
913             }
914         } // end finally
915

916         return decodedData;
917     } // end decodeFromFile
918

919
920     /**
921      * Convenience method for reading a binary file
922      * and base64-encoding it.
923      *
924      * @param filename Filename for reading binary data
925      * @return base64-encoded string or null if unsuccessful
926      * @since 2.1
927      */

928     public static String JavaDoc encodeFromFile(String JavaDoc filename) {
929         String JavaDoc encodedData = null;
930         Base64.InputStream bis = null;
931         try {
932             // Set up some useful variables
933
java.io.File JavaDoc file = new java.io.File JavaDoc(filename);
934             byte[] buffer = new byte[(int) (file.length() * 1.4)];
935             int length = 0;
936             int numBytes = 0;
937
938             // Open a stream
939
bis = new Base64.InputStream(
940                     new java.io.BufferedInputStream JavaDoc(new java.io.FileInputStream JavaDoc(file)), Base64.ENCODE
941             );
942
943             // Read until done
944
while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
945                 length += numBytes;
946             }
947
948             // Save in a variable to return
949
encodedData = new String JavaDoc(buffer, 0, length, Base64.PREFERRED_ENCODING);
950
951         } // end try
952
catch (java.io.IOException JavaDoc e) {
953             System.err.println("Error encoding from file " + filename);
954         } // end catch: IOException
955
finally {
956             try {
957                 bis.close();
958             } catch (Exception JavaDoc e) {
959             }
960         } // end finally
961

962         return encodedData;
963     } // end encodeFromFile
964

965
966
967
968     /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
969
970
971
972     /**
973      * A {@link Base64.InputStream} will read data from another
974      * <tt>java.io.InputStream</tt>, given in the constructor,
975      * and encode/decode to/from Base64 notation on the fly.
976      *
977      * @see Base64
978      * @since 1.3
979      */

980     public static class InputStream extends java.io.FilterInputStream JavaDoc {
981         private boolean encode; // Encoding or decoding
982
private int position; // Current position in the buffer
983
private byte[] buffer; // Small buffer holding converted data
984
private int bufferLength; // Length of buffer (3 or 4)
985
private int numSigBytes; // Number of meaningful bytes in the buffer
986
private int lineLength;
987         private boolean breakLines; // Break lines at less than 80 characters
988

989
990         /**
991          * Constructs a {@link Base64.InputStream} in DECODE mode.
992          *
993          * @param in the <tt>java.io.InputStream</tt> from which to read data.
994          * @since 1.3
995          */

996         public InputStream(java.io.InputStream JavaDoc in) {
997             this(in, DECODE);
998         } // end constructor
999

1000
1001        /**
1002         * Constructs a {@link Base64.InputStream} in
1003         * either ENCODE or DECODE mode.
1004         * <p/>
1005         * Valid options:<pre>
1006         * ENCODE or DECODE: Encode or Decode as data is read.
1007         * DONT_BREAK_LINES: don't break lines at 76 characters
1008         * (only meaningful when encoding)
1009         * <i>Note: Technically, this makes your encoding non-compliant.</i>
1010         * </pre>
1011         * <p/>
1012         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1013         *
1014         * @param in the <tt>java.io.InputStream</tt> from which to read data.
1015         * @param options Specified options
1016         * @see Base64#ENCODE
1017         * @see Base64#DECODE
1018         * @see Base64#DONT_BREAK_LINES
1019         * @since 2.0
1020         */

1021        public InputStream(java.io.InputStream JavaDoc in, int options) {
1022            super(in);
1023            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1024            this.encode = (options & ENCODE) == ENCODE;
1025            this.bufferLength = encode ? 4 : 3;
1026            this.buffer = new byte[bufferLength];
1027            this.position = -1;
1028            this.lineLength = 0;
1029        } // end constructor
1030

1031        /**
1032         * Reads enough of the input stream to convert
1033         * to/from Base64 and returns the next byte.
1034         *
1035         * @return next byte
1036         * @since 1.3
1037         */

1038        public int read() throws java.io.IOException JavaDoc {
1039            // Do we need to get data?
1040
if (position < 0) {
1041                if (encode) {
1042                    byte[] b3 = new byte[3];
1043                    int numBinaryBytes = 0;
1044                    for (int i = 0; i < 3; i++) {
1045                        try {
1046                            int b = in.read();
1047
1048                            // If end of stream, b is -1.
1049
if (b >= 0) {
1050                                b3[i] = (byte) b;
1051                                numBinaryBytes++;
1052                            } // end if: not end of stream
1053

1054                        } // end try: read
1055
catch (java.io.IOException JavaDoc e) {
1056                            // Only a problem if we got no data at all.
1057
if (i == 0) {
1058                                throw e;
1059                            }
1060
1061                        } // end catch
1062
} // end for: each needed input byte
1063

1064                    if (numBinaryBytes > 0) {
1065                        encode3to4(b3, 0, numBinaryBytes, buffer, 0);
1066                        position = 0;
1067                        numSigBytes = 4;
1068                    } // end if: got data
1069
else {
1070                        return -1;
1071                    } // end else
1072
} // end if: encoding
1073

1074                // Else decoding
1075
else {
1076                    byte[] b4 = new byte[4];
1077                    int i = 0;
1078                    for (i = 0; i < 4; i++) {
1079                        // Read four "meaningful" bytes:
1080
int b = 0;
1081                        do {
1082                            b = in.read();
1083                        } while (b >= 0 && DECODABET[b & 0x7f] <= WHITE_SPACE_ENC);
1084
1085                        if (b < 0) {
1086                            break; // Reads a -1 if end of stream
1087
}
1088
1089                        b4[i] = (byte) b;
1090                    } // end for: each needed input byte
1091

1092                    if (i == 4) {
1093                        numSigBytes = decode4to3(b4, 0, buffer, 0);
1094                        position = 0;
1095                    } // end if: got four characters
1096
else if (i == 0) {
1097                        return -1;
1098                    } // end else if: also padded correctly
1099
else {
1100                        // Must have broken out from above.
1101
throw new java.io.IOException JavaDoc("Improperly padded Base64 input.");
1102                    } // end
1103

1104                } // end else: decode
1105
} // end else: get data
1106

1107            // Got data?
1108
if (position >= 0) {
1109                // End of relevant data?
1110
if (/*!encode &&*/ position >= numSigBytes) {
1111                    return -1;
1112                }
1113
1114                if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
1115                    lineLength = 0;
1116                    return '\n';
1117                } // end if
1118
else {
1119                    lineLength++; // This isn't important when decoding
1120
// but throwing an extra "if" seems
1121
// just as wasteful.
1122

1123                    int b = buffer[position++];
1124
1125                    if (position >= bufferLength) {
1126                        position = -1;
1127                    }
1128
1129                    return b & 0xFF; // This is how you "cast" a byte that's
1130
// intended to be unsigned.
1131
} // end else
1132
} // end if: position >= 0
1133

1134            // Else error
1135
else {
1136                // When JDK1.4 is more accepted, use an assertion here.
1137
throw new java.io.IOException JavaDoc("Error in Base64 code reading stream.");
1138            } // end else
1139
} // end read
1140

1141
1142        /**
1143         * Calls {@link #read()} repeatedly until the end of stream
1144         * is reached or <var>len</var> bytes are read.
1145         * Returns number of bytes read into array or -1 if
1146         * end of stream is encountered.
1147         *
1148         * @param dest array to hold values
1149         * @param off offset for array
1150         * @param len max number of bytes to read into array
1151         * @return bytes read into array or -1 if end of stream is encountered.
1152         * @since 1.3
1153         */

1154        public int read(byte[] dest, int off, int len) throws java.io.IOException JavaDoc {
1155            int i;
1156            int b;
1157            for (i = 0; i < len; i++) {
1158                b = read();
1159
1160                //if( b < 0 && i == 0 )
1161
// return -1;
1162

1163                if (b >= 0) {
1164                    dest[off + i] = (byte) b;
1165                } else if (i == 0) {
1166                    return -1;
1167                } else {
1168                    break; // Out of 'for' loop
1169
}
1170            } // end for: each byte read
1171
return i;
1172        } // end read
1173

1174    } // end inner class InputStream
1175

1176
1177
1178
1179
1180
1181    /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1182
1183
1184
1185    /**
1186     * A {@link Base64.OutputStream} will write data to another
1187     * <tt>java.io.OutputStream</tt>, given in the constructor,
1188     * and encode/decode to/from Base64 notation on the fly.
1189     *
1190     * @see Base64
1191     * @since 1.3
1192     */

1193    public static class OutputStream extends java.io.FilterOutputStream JavaDoc {
1194        private boolean encode;
1195        private int position;
1196        private byte[] buffer;
1197        private int bufferLength;
1198        private int lineLength;
1199        private boolean breakLines;
1200        private byte[] b4; // Scratch used in a few places
1201
private boolean suspendEncoding;
1202
1203        /**
1204         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1205         *
1206         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1207         * @since 1.3
1208         */

1209        public OutputStream(java.io.OutputStream JavaDoc out) {
1210            this(out, ENCODE);
1211        } // end constructor
1212

1213
1214        /**
1215         * Constructs a {@link Base64.OutputStream} in
1216         * either ENCODE or DECODE mode.
1217         * <p/>
1218         * Valid options:<pre>
1219         * ENCODE or DECODE: Encode or Decode as data is read.
1220         * DONT_BREAK_LINES: don't break lines at 76 characters
1221         * (only meaningful when encoding)
1222         * <i>Note: Technically, this makes your encoding non-compliant.</i>
1223         * </pre>
1224         * <p/>
1225         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1226         *
1227         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1228         * @param options Specified options.
1229         * @see Base64#ENCODE
1230         * @see Base64#DECODE
1231         * @see Base64#DONT_BREAK_LINES
1232         * @since 1.3
1233         */

1234        public OutputStream(java.io.OutputStream JavaDoc out, int options) {
1235            super(out);
1236            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1237            this.encode = (options & ENCODE) == ENCODE;
1238            this.bufferLength = encode ? 3 : 4;
1239            this.buffer = new byte[bufferLength];
1240            this.position = 0;
1241            this.lineLength = 0;
1242            this.suspendEncoding = false;
1243            this.b4 = new byte[4];
1244        } // end constructor
1245

1246
1247        /**
1248         * Writes the byte to the output stream after
1249         * converting to/from Base64 notation.
1250         * When encoding, bytes are buffered three
1251         * at a time before the output stream actually
1252         * gets a write() call.
1253         * When decoding, bytes are buffered four
1254         * at a time.
1255         *
1256         * @param theByte the byte to write
1257         * @since 1.3
1258         */

1259        public void write(int theByte) throws java.io.IOException JavaDoc {
1260            // Encoding suspended?
1261
if (suspendEncoding) {
1262                super.out.write(theByte);
1263                return;
1264            } // end if: supsended
1265

1266            // Encode?
1267
if (encode) {
1268                buffer[position++] = (byte) theByte;
1269                if (position >= bufferLength) // Enough to encode.
1270
{
1271                    out.write(encode3to4(b4, buffer, bufferLength));
1272
1273                    lineLength += 4;
1274                    if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1275                        out.write(NEW_LINE);
1276                        lineLength = 0;
1277                    } // end if: end of line
1278

1279                    position = 0;
1280                } // end if: enough to output
1281
} // end if: encoding
1282

1283            // Else, Decoding
1284
else {
1285                // Meaningful Base64 character?
1286
if (DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC) {
1287                    buffer[position++] = (byte) theByte;
1288                    if (position >= bufferLength) // Enough to output.
1289
{
1290                        int len = Base64.decode4to3(buffer, 0, b4, 0);
1291                        out.write(b4, 0, len);
1292                        //out.write( Base64.decode4to3( buffer ) );
1293
position = 0;
1294                    } // end if: enough to output
1295
} // end if: meaningful base64 character
1296
else if (DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC) {
1297                    throw new java.io.IOException JavaDoc("Invalid character in Base64 data.");
1298                } // end else: not white space either
1299
} // end else: decoding
1300
} // end write
1301

1302
1303        /**
1304         * Calls {@link #write(int)} repeatedly until <var>len</var>
1305         * bytes are written.
1306         *
1307         * @param theBytes array from which to read bytes
1308         * @param off offset for array
1309         * @param len max number of bytes to read into array
1310         * @since 1.3
1311         */

1312        public void write(byte[] theBytes, int off, int len) throws java.io.IOException JavaDoc {
1313            // Encoding suspended?
1314
if (suspendEncoding) {
1315                super.out.write(theBytes, off, len);
1316                return;
1317            } // end if: supsended
1318

1319            for (int i = 0; i < len; i++) {
1320                write(theBytes[off + i]);
1321            } // end for: each byte written
1322

1323        } // end write
1324

1325
1326        /**
1327         * Method added by PHIL. [Thanks, PHIL. -Rob]
1328         * This pads the buffer without closing the stream.
1329         */

1330        public void flushBase64() throws java.io.IOException JavaDoc {
1331            if (position > 0) {
1332                if (encode) {
1333                    out.write(encode3to4(b4, buffer, position));
1334                    position = 0;
1335                } // end if: encoding
1336
else {
1337                    throw new java.io.IOException JavaDoc("Base64 input not properly padded.");
1338                } // end else: decoding
1339
} // end if: buffer partially full
1340

1341        } // end flush
1342

1343
1344        /**
1345         * Flushes and closes (I think, in the superclass) the stream.
1346         *
1347         * @since 1.3
1348         */

1349        public void close() throws java.io.IOException JavaDoc {
1350            // 1. Ensure that pending characters are written
1351
flushBase64();
1352
1353            // 2. Actually close the stream
1354
// Base class both flushes and closes.
1355
super.close();
1356
1357            buffer = null;
1358            out = null;
1359        } // end close
1360

1361
1362        /**
1363         * Suspends encoding of the stream.
1364         * May be helpful if you need to embed a piece of
1365         * base640-encoded data in a stream.
1366         *
1367         * @since 1.5.2
1368         */

1369        public void suspendEncoding() throws java.io.IOException JavaDoc {
1370            flushBase64();
1371            this.suspendEncoding = true;
1372        } // end suspendEncoding
1373

1374
1375        /**
1376         * Resumes encoding of the stream.
1377         * May be helpful if you need to embed a piece of
1378         * base640-encoded data in a stream.
1379         *
1380         * @since 1.5.2
1381         */

1382        public void resumeEncoding() {
1383            this.suspendEncoding = false;
1384        } // end resumeEncoding
1385

1386
1387    } // end inner class OutputStream
1388

1389
1390} // end class Base64
1391
Popular Tags