KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > polyglot > util > Base64


1 package polyglot.util;
2
3 /**
4  * Encodes and decodes to and from Base64 notation.
5  *
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.1 - 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  *
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     
53 /* ******** P U B L I C F I E L D S ******** */
54     
55     
56     /** No options specified. Value is zero. */
57     public final static int NO_OPTIONS = 0;
58     
59     /** Specify encoding. */
60     public final static int ENCODE = 1;
61     
62     
63     /** Specify decoding. */
64     public final static int DECODE = 0;
65     
66     
67     /** Specify that data should be gzip-compressed. */
68     public final static int GZIP = 2;
69     
70     
71     /** Don't break lines when encoding (violates strict Base64 specification) */
72     public final static int DONT_BREAK_LINES = 8;
73     
74     
75 /* ******** P R I V A T E F I E L D S ******** */
76     
77     
78     /** Maximum line length (76) of Base64 output. */
79     private final static int MAX_LINE_LENGTH = 76;
80     
81     
82     /** The equals sign (=) as a byte. */
83     private final static byte EQUALS_SIGN = (byte)'=';
84     
85     
86     /** The new line character (\n) as a byte. */
87     private final static byte NEW_LINE = (byte)'\n';
88     
89     
90     /** Preferred encoding. */
91     private final static String JavaDoc PREFERRED_ENCODING = "UTF-8";
92     
93     
94     /** The 64 valid Base64 values. */
95     private final static byte[] ALPHABET;
96     private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */
97     {
98         (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
99         (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
100         (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
101         (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
102         (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
103         (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
104         (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
105         (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
106         (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
107         (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
108     };
109     
110     /** Determine which ALPHABET to use. */
111     static
112     {
113         byte[] __bytes;
114         try
115         {
116             __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING );
117         } // end try
118
catch (java.io.UnsupportedEncodingException JavaDoc use)
119         {
120             __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
121
} // end catch
122
ALPHABET = __bytes;
123     } // end static
124

125     
126     /**
127      * Translates a Base64 value to either its 6-bit reconstruction value
128      * or a negative number indicating some other meaning.
129      **/

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

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     /** Defeats instantiation. */
172     private Base64(){}
173     
174     
175     
176 /* ******** E N C O D I N G M E T H O D S ******** */
177     
178     
179     /**
180      * Encodes up to the first three bytes of array <var>threeBytes</var>
181      * and returns a four-byte array in Base64 notation.
182      * The actual number of significant bytes in your array is
183      * given by <var>numSigBytes</var>.
184      * The array <var>threeBytes</var> needs only be as big as
185      * <var>numSigBytes</var>.
186      * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
187      *
188      * @param b4 A reusable byte array to reduce array instantiation
189      * @param threeBytes the array to convert
190      * @param numSigBytes the number of significant bytes in your array
191      * @return four byte array in Base64 notation.
192      * @since 1.5.1
193      */

194     private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes )
195     {
196         encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
197         return b4;
198     } // end encode3to4
199

200     
201     /**
202      * Encodes up to three bytes of the array <var>source</var>
203      * and writes the resulting four Base64 bytes to <var>destination</var>.
204      * The source and destination arrays can be manipulated
205      * anywhere along their length by specifying
206      * <var>srcOffset</var> and <var>destOffset</var>.
207      * This method does not check to make sure your arrays
208      * are large enough to accomodate <var>srcOffset</var> + 3 for
209      * the <var>source</var> array or <var>destOffset</var> + 4 for
210      * the <var>destination</var> array.
211      * The actual number of significant bytes in your array is
212      * given by <var>numSigBytes</var>.
213      *
214      * @param source the array to convert
215      * @param srcOffset the index where conversion begins
216      * @param numSigBytes the number of significant bytes in your array
217      * @param destination the array to hold the conversion
218      * @param destOffset the index where output will be put
219      * @return the <var>destination</var> array
220      * @since 1.3
221      */

222     private static byte[] encode3to4(
223      byte[] source, int srcOffset, int numSigBytes,
224      byte[] destination, int destOffset )
225     {
226         // 1 2 3
227
// 01234567890123456789012345678901 Bit position
228
// --------000000001111111122222222 Array position from threeBytes
229
// --------| || || || | Six bit groups to index ALPHABET
230
// >>18 >>12 >> 6 >> 0 Right shift necessary
231
// 0x3f 0x3f 0x3f Additional AND
232

233         // Create buffer with zero-padding if there are only one or two
234
// significant bytes passed in the array.
235
// We have to shift left 24 in order to flush out the 1's that appear
236
// when Java treats a value as negative that is cast from a byte to an int.
237
int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
238                      | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
239                      | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
240
241         switch( numSigBytes )
242         {
243             case 3:
244                 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
245                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
246                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
247                 destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
248                 return destination;
249                 
250             case 2:
251                 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
252                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
253                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
254                 destination[ destOffset + 3 ] = EQUALS_SIGN;
255                 return destination;
256                 
257             case 1:
258                 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
259                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
260                 destination[ destOffset + 2 ] = EQUALS_SIGN;
261                 destination[ destOffset + 3 ] = EQUALS_SIGN;
262                 return destination;
263                 
264             default:
265                 return destination;
266         } // end switch
267
} // end encode3to4
268

269     
270     
271     /**
272      * Serializes an object and returns the Base64-encoded
273      * version of that serialized object. If the object
274      * cannot be serialized or there is another error,
275      * the method will return <tt>null</tt>.
276      * The object is not GZip-compressed before being encoded.
277      *
278      * @param serializableObject The object to encode
279      * @return The Base64-encoded object
280      * @since 1.4
281      */

282     public static String JavaDoc encodeObject( java.io.Serializable JavaDoc serializableObject )
283     {
284         return encodeObject( serializableObject, NO_OPTIONS );
285     } // end encodeObject
286

287
288
289     /**
290      * Serializes an object and returns the Base64-encoded
291      * version of that serialized object. If the object
292      * cannot be serialized or there is another error,
293      * the method will return <tt>null</tt>.
294      * <p>
295      * Valid options:<pre>
296      * GZIP: gzip-compresses object before encoding it.
297      * DONT_BREAK_LINES: don't break lines at 76 characters
298      * <i>Note: Technically, this makes your encoding non-compliant.</i>
299      * </pre>
300      * <p>
301      * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
302      * <p>
303      * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
304      *
305      * @param serializableObject The object to encode
306      * @param options Specified options
307      * @return The Base64-encoded object
308      * @see Base64#GZIP
309      * @see Base64#DONT_BREAK_LINES
310      * @since 2.0
311      */

312     public static String JavaDoc encodeObject( java.io.Serializable JavaDoc serializableObject, int options )
313     {
314         // Streams
315
java.io.ByteArrayOutputStream JavaDoc baos = null;
316         java.io.OutputStream JavaDoc b64os = null;
317         java.io.ObjectOutputStream JavaDoc oos = null;
318         java.util.zip.GZIPOutputStream JavaDoc gzos = null;
319         
320         // Isolate options
321
int gzip = (options & GZIP);
322         int dontBreakLines = (options & DONT_BREAK_LINES);
323         
324         try
325         {
326             // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
327
baos = new java.io.ByteArrayOutputStream JavaDoc();
328             b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
329     
330             // GZip?
331
if( gzip == GZIP )
332             {
333                 gzos = new java.util.zip.GZIPOutputStream JavaDoc( b64os );
334                 oos = new java.io.ObjectOutputStream JavaDoc( gzos );
335             } // end if: gzip
336
else
337                 oos = new java.io.ObjectOutputStream JavaDoc( b64os );
338             
339             oos.writeObject( serializableObject );
340         } // end try
341
catch( java.io.IOException JavaDoc e )
342         {
343             e.printStackTrace();
344             return null;
345         } // end catch
346
finally
347         {
348             try{ oos.close(); } catch( Exception JavaDoc e ){}
349             try{ gzos.close(); } catch( Exception JavaDoc e ){}
350             try{ b64os.close(); } catch( Exception JavaDoc e ){}
351             try{ baos.close(); } catch( Exception JavaDoc e ){}
352         } // end finally
353

354         // Return value according to relevant encoding.
355
try
356         {
357             return new String JavaDoc( baos.toByteArray(), PREFERRED_ENCODING );
358         } // end try
359
catch (java.io.UnsupportedEncodingException JavaDoc uue)
360         {
361             return new String JavaDoc( baos.toByteArray() );
362         } // end catch
363

364     } // end encode
365

366     
367
368     /**
369      * Encodes a byte array into Base64 notation.
370      * Does not GZip-compress data.
371      *
372      * @param source The data to convert
373      * @since 1.4
374      */

375     public static String JavaDoc encodeBytes( byte[] source )
376     {
377         return encodeBytes( source, 0, source.length, NO_OPTIONS );
378     } // end encodeBytes
379

380
381     /**
382      * Encodes a byte array into Base64 notation.
383      * <p>
384      * Valid options:<pre>
385      * GZIP: gzip-compresses object before encoding it.
386      * DONT_BREAK_LINES: don't break lines at 76 characters
387      * <i>Note: Technically, this makes your encoding non-compliant.</i>
388      * </pre>
389      * <p>
390      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
391      * <p>
392      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
393      *
394      *
395      * @param source The data to convert
396      * @param options Specified options
397      * @see Base64#GZIP
398      * @see Base64#DONT_BREAK_LINES
399      * @since 2.0
400      */

401     public static String JavaDoc encodeBytes( byte[] source, int options )
402     {
403         return encodeBytes( source, 0, source.length, options );
404     } // end encodeBytes
405

406     
407     /**
408      * Encodes a byte array into Base64 notation.
409      * Does not GZip-compress data.
410      *
411      * @param source The data to convert
412      * @param off Offset in array where conversion should begin
413      * @param len Length of data to convert
414      * @since 1.4
415      */

416     public static String JavaDoc encodeBytes( byte[] source, int off, int len )
417     {
418         return encodeBytes( source, off, len, NO_OPTIONS );
419     } // end encodeBytes
420

421     
422
423     /**
424      * Encodes a byte array into Base64 notation.
425      * <p>
426      * Valid options:<pre>
427      * GZIP: gzip-compresses object before encoding it.
428      * DONT_BREAK_LINES: don't break lines at 76 characters
429      * <i>Note: Technically, this makes your encoding non-compliant.</i>
430      * </pre>
431      * <p>
432      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
433      * <p>
434      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
435      *
436      *
437      * @param source The data to convert
438      * @param off Offset in array where conversion should begin
439      * @param len Length of data to convert
440      * @param options Specified options
441      * @see Base64#GZIP
442      * @see Base64#DONT_BREAK_LINES
443      * @since 2.0
444      */

445     public static String JavaDoc encodeBytes( byte[] source, int off, int len, int options )
446     {
447         // Isolate options
448
int dontBreakLines = ( options & DONT_BREAK_LINES );
449         int gzip = ( options & GZIP );
450         
451         // Compress?
452
if( gzip == GZIP )
453         {
454             java.io.ByteArrayOutputStream JavaDoc baos = null;
455             java.util.zip.GZIPOutputStream JavaDoc gzos = null;
456             Base64.OutputStream b64os = null;
457             
458     
459             try
460             {
461                 // GZip -> Base64 -> ByteArray
462
baos = new java.io.ByteArrayOutputStream JavaDoc();
463                 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
464                 gzos = new java.util.zip.GZIPOutputStream JavaDoc( b64os );
465             
466                 gzos.write( source, off, len );
467                 gzos.close();
468             } // end try
469
catch( java.io.IOException JavaDoc e )
470             {
471                 e.printStackTrace();
472                 return null;
473             } // end catch
474
finally
475             {
476                 try{ gzos.close(); } catch( Exception JavaDoc e ){}
477                 try{ b64os.close(); } catch( Exception JavaDoc e ){}
478                 try{ baos.close(); } catch( Exception JavaDoc e ){}
479             } // end finally
480

481             // Return value according to relevant encoding.
482
try
483             {
484                 return new String JavaDoc( baos.toByteArray(), PREFERRED_ENCODING );
485             } // end try
486
catch (java.io.UnsupportedEncodingException JavaDoc uue)
487             {
488                 return new String JavaDoc( baos.toByteArray() );
489             } // end catch
490
} // end if: compress
491

492         // Else, don't compress. Better not to use streams at all then.
493
else
494         {
495             // Convert option to boolean in way that code likes it.
496
boolean breakLines = dontBreakLines == 0;
497             
498             int len43 = len * 4 / 3;
499             byte[] outBuff = new byte[ ( len43 ) // Main 4:3
500
+ ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
501
+ (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
502
int d = 0;
503             int e = 0;
504             int len2 = len - 2;
505             int lineLength = 0;
506             for( ; d < len2; d+=3, e+=4 )
507             {
508                 encode3to4( source, d+off, 3, outBuff, e );
509
510                 lineLength += 4;
511                 if( breakLines && lineLength == MAX_LINE_LENGTH )
512                 {
513                     outBuff[e+4] = NEW_LINE;
514                     e++;
515                     lineLength = 0;
516                 } // end if: end of line
517
} // en dfor: each piece of array
518

519             if( d < len )
520             {
521                 encode3to4( source, d+off, len - d, outBuff, e );
522                 e += 4;
523             } // end if: some padding needed
524

525             
526             // Return value according to relevant encoding.
527
try
528             {
529                 return new String JavaDoc( outBuff, 0, e, PREFERRED_ENCODING );
530             } // end try
531
catch (java.io.UnsupportedEncodingException JavaDoc uue)
532             {
533                 return new String JavaDoc( outBuff, 0, e );
534             } // end catch
535

536         } // end else: don't compress
537

538     } // end encodeBytes
539

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

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

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

644     public static byte[] decode( byte[] source, int off, int len )
645     {
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         {
657             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
658
sbiDecode = DECODABET[ sbiCrop ];
659             
660             if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
661
{
662                 if( sbiDecode >= EQUALS_SIGN_ENC )
663                 {
664                     b4[ b4Posn++ ] = sbiCrop;
665                     if( b4Posn > 3 )
666                     {
667                         outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
668                         b4Posn = 0;
669                         
670                         // If that was the equals sign, break out of 'for' loop
671
if( sbiCrop == EQUALS_SIGN )
672                             break;
673                     } // end if: quartet built
674

675                 } // end if: equals sign or better
676

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

685         byte[] out = new byte[ outBuffPosn ];
686         System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
687         return out;
688     } // end decode
689

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

701     public static byte[] decode( String JavaDoc s )
702     {
703         byte[] bytes;
704         try
705         {
706             bytes = s.getBytes( PREFERRED_ENCODING );
707         } // end try
708
catch( java.io.UnsupportedEncodingException JavaDoc uee )
709         {
710             bytes = s.getBytes();
711         } // end catch
712
//</change>
713

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

743                     // No error? Get new bytes.
744
bytes = baos.toByteArray();
745
746                 } // end try
747
catch( java.io.IOException JavaDoc e )
748                 {
749                     // Just return originally-decoded bytes
750
} // end catch
751
finally
752                 {
753                     try{ baos.close(); } catch( Exception JavaDoc e ){}
754                     try{ gzis.close(); } catch( Exception JavaDoc e ){}
755                     try{ bais.close(); } catch( Exception JavaDoc e ){}
756                 } // end finally
757

758             } // end if: gzipped
759
} // end if: bytes.length >= 2
760

761         return bytes;
762     } // end decode
763

764
765     
766
767     /**
768      * Attempts to decode Base64 data and deserialize a Java
769      * Object within. Returns <tt>null</tt> if there was an error.
770      *
771      * @param encodedObject The Base64 data to decode
772      * @return The decoded and deserialized object
773      * @since 1.5
774      */

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

807         return obj;
808     } // end decodeObject
809

810     
811     
812     /**
813      * Convenience method for encoding data to a file.
814      *
815      * @param dataToEncode byte array of data to encode in base64 form
816      * @param filename Filename for saving encoded data
817      * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
818      *
819      * @since 2.1
820      */

821     public static boolean encodeToFile( byte[] dataToEncode, String JavaDoc filename )
822     {
823         boolean success = false;
824         Base64.OutputStream bos = null;
825         try
826         {
827             bos = new Base64.OutputStream(
828                       new java.io.FileOutputStream JavaDoc( filename ), Base64.ENCODE );
829             bos.write( dataToEncode );
830             success = true;
831         } // end try
832
catch( java.io.IOException JavaDoc e )
833         {
834             
835             success = false;
836         } // end catch: IOException
837
finally
838         {
839             try{ bos.close(); } catch( Exception JavaDoc e ){}
840         } // end finally
841

842         return success;
843     } // end encodeToFile
844

845     
846     /**
847      * Convenience method for decoding data to a file.
848      *
849      * @param dataToDecode Base64-encoded data as a string
850      * @param filename Filename for saving decoded data
851      * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
852      *
853      * @since 2.1
854      */

855     public static boolean decodeToFile( String JavaDoc dataToDecode, String JavaDoc filename )
856     {
857         boolean success = false;
858         Base64.OutputStream bos = null;
859         try
860         {
861                 bos = new Base64.OutputStream(
862                           new java.io.FileOutputStream JavaDoc( filename ), Base64.DECODE );
863                 bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
864                 success = true;
865         } // end try
866
catch( java.io.IOException JavaDoc e )
867         {
868             success = false;
869         } // end catch: IOException
870
finally
871         {
872                 try{ bos.close(); } catch( Exception JavaDoc e ){}
873         } // end finally
874

875         return success;
876     } // end decodeToFile
877

878     
879     
880     
881     /**
882      * Convenience method for reading a base64-encoded
883      * file and decoding it.
884      *
885      * @param filename Filename for reading encoded data
886      * @return decoded byte array or null if unsuccessful
887      *
888      * @since 2.1
889      */

890     public static byte[] decodeFromFile( String JavaDoc filename )
891     {
892         byte[] decodedData = null;
893         Base64.InputStream bis = null;
894         try
895         {
896             // Set up some useful variables
897
java.io.File JavaDoc file = new java.io.File JavaDoc( filename );
898             byte[] buffer = null;
899             int length = 0;
900             int numBytes = 0;
901             
902             // Check for size of file
903
if( file.length() > Integer.MAX_VALUE )
904             {
905                 System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." );
906                 return null;
907             } // end if: file too big for int index
908
buffer = new byte[ (int)file.length() ];
909             
910             // Open a stream
911
bis = new Base64.InputStream(
912                       new java.io.BufferedInputStream JavaDoc(
913                       new java.io.FileInputStream JavaDoc( file ) ), Base64.DECODE );
914             
915             // Read until done
916
while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
917                 length += numBytes;
918             
919             // Save in a variable to return
920
decodedData = new byte[ length ];
921             System.arraycopy( buffer, 0, decodedData, 0, length );
922             
923         } // end try
924
catch( java.io.IOException JavaDoc e )
925         {
926             System.err.println( "Error decoding from file " + filename );
927         } // end catch: IOException
928
finally
929         {
930             try{ bis.close(); } catch( Exception JavaDoc e) {}
931         } // end finally
932

933         return decodedData;
934     } // end decodeFromFile
935

936     
937     
938     /**
939      * Convenience method for reading a binary file
940      * and base64-encoding it.
941      *
942      * @param filename Filename for reading binary data
943      * @return base64-encoded string or null if unsuccessful
944      *
945      * @since 2.1
946      */

947     public static String JavaDoc encodeFromFile( String JavaDoc filename )
948     {
949         String JavaDoc encodedData = null;
950         Base64.InputStream bis = null;
951         try
952         {
953             // Set up some useful variables
954
java.io.File JavaDoc file = new java.io.File JavaDoc( filename );
955             byte[] buffer = new byte[ (int)(file.length() * 1.4) ];
956             int length = 0;
957             int numBytes = 0;
958             
959             // Open a stream
960
bis = new Base64.InputStream(
961                       new java.io.BufferedInputStream JavaDoc(
962                       new java.io.FileInputStream JavaDoc( file ) ), Base64.ENCODE );
963             
964             // Read until done
965
while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
966                 length += numBytes;
967             
968             // Save in a variable to return
969
encodedData = new String JavaDoc( buffer, 0, length, Base64.PREFERRED_ENCODING );
970                 
971         } // end try
972
catch( java.io.IOException JavaDoc e )
973         {
974             System.err.println( "Error encoding from file " + filename );
975         } // end catch: IOException
976
finally
977         {
978             try{ bis.close(); } catch( Exception JavaDoc e) {}
979         } // end finally
980

981         return encodedData;
982         } // end encodeFromFile
983

984     
985
986     /**
987      * Encodes a byte array into Base64 notation.
988      * Does not GZip-compress data.
989      * Added for backward compatibility with old Base64 implementation.
990      * --nystrom
991      *
992      * @param data The data to convert
993      */

994     public static char[] encode(byte[] data)
995     {
996         return encodeBytes(data).toCharArray();
997     }
998         
999
1000    /**
1001     * Decodes data from Base64 notation, automatically
1002     * detecting gzip-compressed data and decompressing it.
1003     * Added for backward compatibility with old Base64 implementation.
1004     * --nystrom
1005     *
1006     * @param data the character array to decode
1007     * @return the decoded data
1008     */

1009    public static byte[] decode(char[] data)
1010    {
1011        return decode(new String JavaDoc(data));
1012    }
1013    
1014    
1015    
1016    /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
1017    
1018    
1019    
1020    /**
1021     * A {@link Base64.InputStream} will read data from another
1022     * <tt>java.io.InputStream</tt>, given in the constructor,
1023     * and encode/decode to/from Base64 notation on the fly.
1024     *
1025     * @see Base64
1026     * @since 1.3
1027     */

1028    public static class InputStream extends java.io.FilterInputStream JavaDoc
1029    {
1030        private boolean encode; // Encoding or decoding
1031
private int position; // Current position in the buffer
1032
private byte[] buffer; // Small buffer holding converted data
1033
private int bufferLength; // Length of buffer (3 or 4)
1034
private int numSigBytes; // Number of meaningful bytes in the buffer
1035
private int lineLength;
1036        private boolean breakLines; // Break lines at less than 80 characters
1037

1038        
1039        /**
1040         * Constructs a {@link Base64.InputStream} in DECODE mode.
1041         *
1042         * @param in the <tt>java.io.InputStream</tt> from which to read data.
1043         * @since 1.3
1044         */

1045        public InputStream( java.io.InputStream JavaDoc in )
1046        {
1047            this( in, DECODE );
1048        } // end constructor
1049

1050        
1051        /**
1052         * Constructs a {@link Base64.InputStream} in
1053         * either ENCODE or DECODE mode.
1054         * <p>
1055         * Valid options:<pre>
1056         * ENCODE or DECODE: Encode or Decode as data is read.
1057         * DONT_BREAK_LINES: don't break lines at 76 characters
1058         * (only meaningful when encoding)
1059         * <i>Note: Technically, this makes your encoding non-compliant.</i>
1060         * </pre>
1061         * <p>
1062         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1063         *
1064         *
1065         * @param in the <tt>java.io.InputStream</tt> from which to read data.
1066         * @param options Specified options
1067         * @see Base64#ENCODE
1068         * @see Base64#DECODE
1069         * @see Base64#DONT_BREAK_LINES
1070         * @since 2.0
1071         */

1072        public InputStream( java.io.InputStream JavaDoc in, int options )
1073        {
1074            super( in );
1075            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1076            this.encode = (options & ENCODE) == ENCODE;
1077            this.bufferLength = encode ? 4 : 3;
1078            this.buffer = new byte[ bufferLength ];
1079            this.position = -1;
1080            this.lineLength = 0;
1081        } // end constructor
1082

1083        /**
1084         * Reads enough of the input stream to convert
1085         * to/from Base64 and returns the next byte.
1086         *
1087         * @return next byte
1088         * @since 1.3
1089         */

1090        public int read() throws java.io.IOException JavaDoc
1091        {
1092            // Do we need to get data?
1093
if( position < 0 )
1094            {
1095                if( encode )
1096                {
1097                    byte[] b3 = new byte[3];
1098                    int numBinaryBytes = 0;
1099                    for( int i = 0; i < 3; i++ )
1100                    {
1101                        try
1102                        {
1103                            int b = in.read();
1104                            
1105                            // If end of stream, b is -1.
1106
if( b >= 0 )
1107                            {
1108                                b3[i] = (byte)b;
1109                                numBinaryBytes++;
1110                            } // end if: not end of stream
1111

1112                        } // end try: read
1113
catch( java.io.IOException JavaDoc e )
1114                        {
1115                            // Only a problem if we got no data at all.
1116
if( i == 0 )
1117                                throw e;
1118                            
1119                        } // end catch
1120
} // end for: each needed input byte
1121

1122                    if( numBinaryBytes > 0 )
1123                    {
1124                        encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
1125                        position = 0;
1126                        numSigBytes = 4;
1127                    } // end if: got data
1128
else
1129                    {
1130                        return -1;
1131                    } // end else
1132
} // end if: encoding
1133

1134                // Else decoding
1135
else
1136                {
1137                    byte[] b4 = new byte[4];
1138                    int i = 0;
1139                    for( i = 0; i < 4; i++ )
1140                    {
1141                        // Read four "meaningful" bytes:
1142
int b = 0;
1143                        do{ b = in.read(); }
1144                        while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
1145                        
1146                        if( b < 0 )
1147                            break; // Reads a -1 if end of stream
1148

1149                        b4[i] = (byte)b;
1150                    } // end for: each needed input byte
1151

1152                    if( i == 4 )
1153                    {
1154                        numSigBytes = decode4to3( b4, 0, buffer, 0 );
1155                        position = 0;
1156                    } // end if: got four characters
1157
else if( i == 0 ){
1158                        return -1;
1159                    } // end else if: also padded correctly
1160
else
1161                    {
1162                        // Must have broken out from above.
1163
throw new java.io.IOException JavaDoc( "Improperly padded Base64 input." );
1164                    } // end
1165

1166                } // end else: decode
1167
} // end else: get data
1168

1169            // Got data?
1170
if( position >= 0 )
1171            {
1172                // End of relevant data?
1173
if( /*!encode &&*/ position >= numSigBytes )
1174                    return -1;
1175                
1176                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
1177                {
1178                    lineLength = 0;
1179                    return '\n';
1180                } // end if
1181
else
1182                {
1183                    lineLength++; // This isn't important when decoding
1184
// but throwing an extra "if" seems
1185
// just as wasteful.
1186

1187                    int b = buffer[ position++ ];
1188
1189                    if( position >= bufferLength )
1190                        position = -1;
1191
1192                    return b & 0xFF; // This is how you "cast" a byte that's
1193
// intended to be unsigned.
1194
} // end else
1195
} // end if: position >= 0
1196

1197            // Else error
1198
else
1199            {
1200                // When JDK1.4 is more accepted, use an assertion here.
1201
throw new java.io.IOException JavaDoc( "Error in Base64 code reading stream." );
1202            } // end else
1203
} // end read
1204

1205        
1206        /**
1207         * Calls {@link #read()} repeatedly until the end of stream
1208         * is reached or <var>len</var> bytes are read.
1209         * Returns number of bytes read into array or -1 if
1210         * end of stream is encountered.
1211         *
1212         * @param dest array to hold values
1213         * @param off offset for array
1214         * @param len max number of bytes to read into array
1215         * @return bytes read into array or -1 if end of stream is encountered.
1216         * @since 1.3
1217         */

1218        public int read( byte[] dest, int off, int len ) throws java.io.IOException JavaDoc
1219        {
1220            int i;
1221            int b;
1222            for( i = 0; i < len; i++ )
1223            {
1224                b = read();
1225                
1226                //if( b < 0 && i == 0 )
1227
// return -1;
1228

1229                if( b >= 0 )
1230                    dest[off + i] = (byte)b;
1231                else if( i == 0 )
1232                    return -1;
1233                else
1234                    break; // Out of 'for' loop
1235
} // end for: each byte read
1236
return i;
1237        } // end read
1238

1239    } // end inner class InputStream
1240

1241    
1242    
1243    
1244    
1245    
1246    /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1247    
1248    
1249    
1250    /**
1251     * A {@link Base64.OutputStream} will write data to another
1252     * <tt>java.io.OutputStream</tt>, given in the constructor,
1253     * and encode/decode to/from Base64 notation on the fly.
1254     *
1255     * @see Base64
1256     * @since 1.3
1257     */

1258    public static class OutputStream extends java.io.FilterOutputStream JavaDoc
1259    {
1260        private boolean encode;
1261        private int position;
1262        private byte[] buffer;
1263        private int bufferLength;
1264        private int lineLength;
1265        private boolean breakLines;
1266        private byte[] b4; // Scratch used in a few places
1267
private boolean suspendEncoding;
1268        
1269        /**
1270         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1271         *
1272         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1273         * @since 1.3
1274         */

1275        public OutputStream( java.io.OutputStream JavaDoc out )
1276        {
1277            this( out, ENCODE );
1278        } // end constructor
1279

1280        
1281        /**
1282         * Constructs a {@link Base64.OutputStream} in
1283         * either ENCODE or DECODE mode.
1284         * <p>
1285         * Valid options:<pre>
1286         * ENCODE or DECODE: Encode or Decode as data is read.
1287         * DONT_BREAK_LINES: don't break lines at 76 characters
1288         * (only meaningful when encoding)
1289         * <i>Note: Technically, this makes your encoding non-compliant.</i>
1290         * </pre>
1291         * <p>
1292         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1293         *
1294         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1295         * @param options Specified options.
1296         * @see Base64#ENCODE
1297         * @see Base64#DECODE
1298         * @see Base64#DONT_BREAK_LINES
1299         * @since 1.3
1300         */

1301        public OutputStream( java.io.OutputStream JavaDoc out, int options )
1302        {
1303            super( out );
1304            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1305            this.encode = (options & ENCODE) == ENCODE;
1306            this.bufferLength = encode ? 3 : 4;
1307            this.buffer = new byte[ bufferLength ];
1308            this.position = 0;
1309            this.lineLength = 0;
1310            this.suspendEncoding = false;
1311            this.b4 = new byte[4];
1312        } // end constructor
1313

1314        
1315        /**
1316         * Writes the byte to the output stream after
1317         * converting to/from Base64 notation.
1318         * When encoding, bytes are buffered three
1319         * at a time before the output stream actually
1320         * gets a write() call.
1321         * When decoding, bytes are buffered four
1322         * at a time.
1323         *
1324         * @param theByte the byte to write
1325         * @since 1.3
1326         */

1327        public void write(int theByte) throws java.io.IOException JavaDoc
1328        {
1329            // Encoding suspended?
1330
if( suspendEncoding )
1331            {
1332                super.out.write( theByte );
1333                return;
1334            } // end if: supsended
1335

1336            // Encode?
1337
if( encode )
1338            {
1339                buffer[ position++ ] = (byte)theByte;
1340                if( position >= bufferLength ) // Enough to encode.
1341
{
1342                    out.write( encode3to4( b4, buffer, bufferLength ) );
1343
1344                    lineLength += 4;
1345                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
1346                    {
1347                        out.write( NEW_LINE );
1348                        lineLength = 0;
1349                    } // end if: end of line
1350

1351                    position = 0;
1352                } // end if: enough to output
1353
} // end if: encoding
1354

1355            // Else, Decoding
1356
else
1357            {
1358                // Meaningful Base64 character?
1359
if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1360                {
1361                    buffer[ position++ ] = (byte)theByte;
1362                    if( position >= bufferLength ) // Enough to output.
1363
{
1364                        int len = Base64.decode4to3( buffer, 0, b4, 0 );
1365                        out.write( b4, 0, len );
1366                        //out.write( Base64.decode4to3( buffer ) );
1367
position = 0;
1368                    } // end if: enough to output
1369
} // end if: meaningful base64 character
1370
else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1371                {
1372                    throw new java.io.IOException JavaDoc( "Invalid character in Base64 data." );
1373                } // end else: not white space either
1374
} // end else: decoding
1375
} // end write
1376

1377        
1378        
1379        /**
1380         * Calls {@link #write(int)} repeatedly until <var>len</var>
1381         * bytes are written.
1382         *
1383         * @param theBytes array from which to read bytes
1384         * @param off offset for array
1385         * @param len max number of bytes to read into array
1386         * @since 1.3
1387         */

1388        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException JavaDoc
1389        {
1390            // Encoding suspended?
1391
if( suspendEncoding )
1392            {
1393                super.out.write( theBytes, off, len );
1394                return;
1395            } // end if: supsended
1396

1397            for( int i = 0; i < len; i++ )
1398            {
1399                write( theBytes[ off + i ] );
1400            } // end for: each byte written
1401

1402        } // end write
1403

1404        
1405        
1406        /**
1407         * Method added by PHIL. [Thanks, PHIL. -Rob]
1408         * This pads the buffer without closing the stream.
1409         */

1410        public void flushBase64() throws java.io.IOException JavaDoc
1411        {
1412            if( position > 0 )
1413            {
1414                if( encode )
1415                {
1416                    out.write( encode3to4( b4, buffer, position ) );
1417                    position = 0;
1418                } // end if: encoding
1419
else
1420                {
1421                    throw new java.io.IOException JavaDoc( "Base64 input not properly padded." );
1422                } // end else: decoding
1423
} // end if: buffer partially full
1424

1425        } // end flush
1426

1427        
1428        /**
1429         * Flushes and closes (I think, in the superclass) the stream.
1430         *
1431         * @since 1.3
1432         */

1433        public void close() throws java.io.IOException JavaDoc
1434        {
1435            // 1. Ensure that pending characters are written
1436
flushBase64();
1437
1438            // 2. Actually close the stream
1439
// Base class both flushes and closes.
1440
super.close();
1441            
1442            buffer = null;
1443            out = null;
1444        } // end close
1445

1446        
1447        
1448        /**
1449         * Suspends encoding of the stream.
1450         * May be helpful if you need to embed a piece of
1451         * base640-encoded data in a stream.
1452         *
1453         * @since 1.5.1
1454         */

1455        public void suspendEncoding() throws java.io.IOException JavaDoc
1456        {
1457            flushBase64();
1458            this.suspendEncoding = true;
1459        } // end suspendEncoding
1460

1461        
1462        /**
1463         * Resumes encoding of the stream.
1464         * May be helpful if you need to embed a piece of
1465         * base640-encoded data in a stream.
1466         *
1467         * @since 1.5.1
1468         */

1469        public void resumeEncoding()
1470        {
1471            this.suspendEncoding = false;
1472        } // end resumeEncoding
1473

1474        
1475        
1476    } // end inner class OutputStream
1477

1478    
1479} // end class Base64
1480
Popular Tags