KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > rift > coad > lib > thirdparty > base64 > Base64


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

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

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

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

164     };
165     
166     // I think I end up not using the BAD_ENCODING indicator.
167
//private final static byte BAD_ENCODING = -9; // Indicates error in encoding
168
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
169
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
170

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

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

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

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

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

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

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

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

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

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

365     } // end encode
366

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

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

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

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

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

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

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

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

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

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

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

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

538         } // end else: don't compress
539

540     } // end encodeBytes
541

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

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

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

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

677                 } // end if: equals sign or better
678

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

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

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

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

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

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

760             } // end if: gzipped
761
} // end if: bytes.length >= 2
762

763         return bytes;
764     } // end decode
765

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

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

809         return obj;
810     } // end decodeObject
811

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

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

844         return success;
845     } // end encodeToFile
846

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

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

877         return success;
878     } // end decodeToFile
879

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

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

935         return decodedData;
936     } // end decodeFromFile
937

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

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

983         return encodedData;
984         } // end encodeFromFile
985

986     
987     
988     
989     /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
990     
991     
992     
993     /**
994      * A {@link Base64.InputStream} will read data from another
995      * <tt>java.io.InputStream</tt>, given in the constructor,
996      * and encode/decode to/from Base64 notation on the fly.
997      *
998      * @see Base64
999      * @since 1.3
1000     */

1001    public static class InputStream extends java.io.FilterInputStream JavaDoc
1002    {
1003        private boolean encode; // Encoding or decoding
1004
private int position; // Current position in the buffer
1005
private byte[] buffer; // Small buffer holding converted data
1006
private int bufferLength; // Length of buffer (3 or 4)
1007
private int numSigBytes; // Number of meaningful bytes in the buffer
1008
private int lineLength;
1009        private boolean breakLines; // Break lines at less than 80 characters
1010

1011        
1012        /**
1013         * Constructs a {@link Base64.InputStream} in DECODE mode.
1014         *
1015         * @param in the <tt>java.io.InputStream</tt> from which to read data.
1016         * @since 1.3
1017         */

1018        public InputStream( java.io.InputStream JavaDoc in )
1019        {
1020            this( in, DECODE );
1021        } // end constructor
1022

1023        
1024        /**
1025         * Constructs a {@link Base64.InputStream} in
1026         * either ENCODE or DECODE mode.
1027         * <p>
1028         * Valid options:<pre>
1029         * ENCODE or DECODE: Encode or Decode as data is read.
1030         * DONT_BREAK_LINES: don't break lines at 76 characters
1031         * (only meaningful when encoding)
1032         * <i>Note: Technically, this makes your encoding non-compliant.</i>
1033         * </pre>
1034         * <p>
1035         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1036         *
1037         *
1038         * @param in the <tt>java.io.InputStream</tt> from which to read data.
1039         * @param options Specified options
1040         * @see Base64#ENCODE
1041         * @see Base64#DECODE
1042         * @see Base64#DONT_BREAK_LINES
1043         * @since 2.0
1044         */

1045        public InputStream( java.io.InputStream JavaDoc in, int options )
1046        {
1047            super( in );
1048            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1049            this.encode = (options & ENCODE) == ENCODE;
1050            this.bufferLength = encode ? 4 : 3;
1051            this.buffer = new byte[ bufferLength ];
1052            this.position = -1;
1053            this.lineLength = 0;
1054        } // end constructor
1055

1056        /**
1057         * Reads enough of the input stream to convert
1058         * to/from Base64 and returns the next byte.
1059         *
1060         * @return next byte
1061         * @since 1.3
1062         */

1063        public int read() throws java.io.IOException JavaDoc
1064        {
1065            // Do we need to get data?
1066
if( position < 0 )
1067            {
1068                if( encode )
1069                {
1070                    byte[] b3 = new byte[3];
1071                    int numBinaryBytes = 0;
1072                    for( int i = 0; i < 3; i++ )
1073                    {
1074                        try
1075                        {
1076                            int b = in.read();
1077                            
1078                            // If end of stream, b is -1.
1079
if( b >= 0 )
1080                            {
1081                                b3[i] = (byte)b;
1082                                numBinaryBytes++;
1083                            } // end if: not end of stream
1084

1085                        } // end try: read
1086
catch( java.io.IOException JavaDoc e )
1087                        {
1088                            // Only a problem if we got no data at all.
1089
if( i == 0 )
1090                                throw e;
1091                            
1092                        } // end catch
1093
} // end for: each needed input byte
1094

1095                    if( numBinaryBytes > 0 )
1096                    {
1097                        encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
1098                        position = 0;
1099                        numSigBytes = 4;
1100                    } // end if: got data
1101
else
1102                    {
1103                        return -1;
1104                    } // end else
1105
} // end if: encoding
1106

1107                // Else decoding
1108
else
1109                {
1110                    byte[] b4 = new byte[4];
1111                    int i = 0;
1112                    for( i = 0; i < 4; i++ )
1113                    {
1114                        // Read four "meaningful" bytes:
1115
int b = 0;
1116                        do{ b = in.read(); }
1117                        while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
1118                        
1119                        if( b < 0 )
1120                            break; // Reads a -1 if end of stream
1121

1122                        b4[i] = (byte)b;
1123                    } // end for: each needed input byte
1124

1125                    if( i == 4 )
1126                    {
1127                        numSigBytes = decode4to3( b4, 0, buffer, 0 );
1128                        position = 0;
1129                    } // end if: got four characters
1130
else if( i == 0 ){
1131                        return -1;
1132                    } // end else if: also padded correctly
1133
else
1134                    {
1135                        // Must have broken out from above.
1136
throw new java.io.IOException JavaDoc( "Improperly padded Base64 input." );
1137                    } // end
1138

1139                } // end else: decode
1140
} // end else: get data
1141

1142            // Got data?
1143
if( position >= 0 )
1144            {
1145                // End of relevant data?
1146
if( /*!encode &&*/ position >= numSigBytes )
1147                    return -1;
1148                
1149                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
1150                {
1151                    lineLength = 0;
1152                    return '\n';
1153                } // end if
1154
else
1155                {
1156                    lineLength++; // This isn't important when decoding
1157
// but throwing an extra "if" seems
1158
// just as wasteful.
1159

1160                    int b = buffer[ position++ ];
1161
1162                    if( position >= bufferLength )
1163                        position = -1;
1164
1165                    return b & 0xFF; // This is how you "cast" a byte that's
1166
// intended to be unsigned.
1167
} // end else
1168
} // end if: position >= 0
1169

1170            // Else error
1171
else
1172            {
1173                // When JDK1.4 is more accepted, use an assertion here.
1174
throw new java.io.IOException JavaDoc( "Error in Base64 code reading stream." );
1175            } // end else
1176
} // end read
1177

1178        
1179        /**
1180         * Calls {@link #read()} repeatedly until the end of stream
1181         * is reached or <var>len</var> bytes are read.
1182         * Returns number of bytes read into array or -1 if
1183         * end of stream is encountered.
1184         *
1185         * @param dest array to hold values
1186         * @param off offset for array
1187         * @param len max number of bytes to read into array
1188         * @return bytes read into array or -1 if end of stream is encountered.
1189         * @since 1.3
1190         */

1191        public int read( byte[] dest, int off, int len ) throws java.io.IOException JavaDoc
1192        {
1193            int i;
1194            int b;
1195            for( i = 0; i < len; i++ )
1196            {
1197                b = read();
1198                
1199                //if( b < 0 && i == 0 )
1200
// return -1;
1201

1202                if( b >= 0 )
1203                    dest[off + i] = (byte)b;
1204                else if( i == 0 )
1205                    return -1;
1206                else
1207                    break; // Out of 'for' loop
1208
} // end for: each byte read
1209
return i;
1210        } // end read
1211

1212    } // end inner class InputStream
1213

1214    
1215    
1216    
1217    
1218    
1219    /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1220    
1221    
1222    
1223    /**
1224     * A {@link Base64.OutputStream} will write data to another
1225     * <tt>java.io.OutputStream</tt>, given in the constructor,
1226     * and encode/decode to/from Base64 notation on the fly.
1227     *
1228     * @see Base64
1229     * @since 1.3
1230     */

1231    public static class OutputStream extends java.io.FilterOutputStream JavaDoc
1232    {
1233        private boolean encode;
1234        private int position;
1235        private byte[] buffer;
1236        private int bufferLength;
1237        private int lineLength;
1238        private boolean breakLines;
1239        private byte[] b4; // Scratch used in a few places
1240
private boolean suspendEncoding;
1241        
1242        /**
1243         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1244         *
1245         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1246         * @since 1.3
1247         */

1248        public OutputStream( java.io.OutputStream JavaDoc out )
1249        {
1250            this( out, ENCODE );
1251        } // end constructor
1252

1253        
1254        /**
1255         * Constructs a {@link Base64.OutputStream} in
1256         * either ENCODE or DECODE mode.
1257         * <p>
1258         * Valid options:<pre>
1259         * ENCODE or DECODE: Encode or Decode as data is read.
1260         * DONT_BREAK_LINES: don't break lines at 76 characters
1261         * (only meaningful when encoding)
1262         * <i>Note: Technically, this makes your encoding non-compliant.</i>
1263         * </pre>
1264         * <p>
1265         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1266         *
1267         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1268         * @param options Specified options.
1269         * @see Base64#ENCODE
1270         * @see Base64#DECODE
1271         * @see Base64#DONT_BREAK_LINES
1272         * @since 1.3
1273         */

1274        public OutputStream( java.io.OutputStream JavaDoc out, int options )
1275        {
1276            super( out );
1277            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1278            this.encode = (options & ENCODE) == ENCODE;
1279            this.bufferLength = encode ? 3 : 4;
1280            this.buffer = new byte[ bufferLength ];
1281            this.position = 0;
1282            this.lineLength = 0;
1283            this.suspendEncoding = false;
1284            this.b4 = new byte[4];
1285        } // end constructor
1286

1287        
1288        /**
1289         * Writes the byte to the output stream after
1290         * converting to/from Base64 notation.
1291         * When encoding, bytes are buffered three
1292         * at a time before the output stream actually
1293         * gets a write() call.
1294         * When decoding, bytes are buffered four
1295         * at a time.
1296         *
1297         * @param theByte the byte to write
1298         * @since 1.3
1299         */

1300        public void write(int theByte) throws java.io.IOException JavaDoc
1301        {
1302            // Encoding suspended?
1303
if( suspendEncoding )
1304            {
1305                super.out.write( theByte );
1306                return;
1307            } // end if: supsended
1308

1309            // Encode?
1310
if( encode )
1311            {
1312                buffer[ position++ ] = (byte)theByte;
1313                if( position >= bufferLength ) // Enough to encode.
1314
{
1315                    out.write( encode3to4( b4, buffer, bufferLength ) );
1316
1317                    lineLength += 4;
1318                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
1319                    {
1320                        out.write( NEW_LINE );
1321                        lineLength = 0;
1322                    } // end if: end of line
1323

1324                    position = 0;
1325                } // end if: enough to output
1326
} // end if: encoding
1327

1328            // Else, Decoding
1329
else
1330            {
1331                // Meaningful Base64 character?
1332
if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1333                {
1334                    buffer[ position++ ] = (byte)theByte;
1335                    if( position >= bufferLength ) // Enough to output.
1336
{
1337                        int len = Base64.decode4to3( buffer, 0, b4, 0 );
1338                        out.write( b4, 0, len );
1339                        //out.write( Base64.decode4to3( buffer ) );
1340
position = 0;
1341                    } // end if: enough to output
1342
} // end if: meaningful base64 character
1343
else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1344                {
1345                    throw new java.io.IOException JavaDoc( "Invalid character in Base64 data." );
1346                } // end else: not white space either
1347
} // end else: decoding
1348
} // end write
1349

1350        
1351        
1352        /**
1353         * Calls {@link #write(int)} repeatedly until <var>len</var>
1354         * bytes are written.
1355         *
1356         * @param theBytes array from which to read bytes
1357         * @param off offset for array
1358         * @param len max number of bytes to read into array
1359         * @since 1.3
1360         */

1361        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException JavaDoc
1362        {
1363            // Encoding suspended?
1364
if( suspendEncoding )
1365            {
1366                super.out.write( theBytes, off, len );
1367                return;
1368            } // end if: supsended
1369

1370            for( int i = 0; i < len; i++ )
1371            {
1372                write( theBytes[ off + i ] );
1373            } // end for: each byte written
1374

1375        } // end write
1376

1377        
1378        
1379        /**
1380         * Method added by PHIL. [Thanks, PHIL. -Rob]
1381         * This pads the buffer without closing the stream.
1382         */

1383        public void flushBase64() throws java.io.IOException JavaDoc
1384        {
1385            if( position > 0 )
1386            {
1387                if( encode )
1388                {
1389                    out.write( encode3to4( b4, buffer, position ) );
1390                    position = 0;
1391                } // end if: encoding
1392
else
1393                {
1394                    throw new java.io.IOException JavaDoc( "Base64 input not properly padded." );
1395                } // end else: decoding
1396
} // end if: buffer partially full
1397

1398        } // end flush
1399

1400        
1401        /**
1402         * Flushes and closes (I think, in the superclass) the stream.
1403         *
1404         * @since 1.3
1405         */

1406        public void close() throws java.io.IOException JavaDoc
1407        {
1408            // 1. Ensure that pending characters are written
1409
flushBase64();
1410
1411            // 2. Actually close the stream
1412
// Base class both flushes and closes.
1413
super.close();
1414            
1415            buffer = null;
1416            out = null;
1417        } // end close
1418

1419        
1420        
1421        /**
1422         * Suspends encoding of the stream.
1423         * May be helpful if you need to embed a piece of
1424         * base640-encoded data in a stream.
1425         *
1426         * @since 1.5.1
1427         */

1428        public void suspendEncoding() throws java.io.IOException JavaDoc
1429        {
1430            flushBase64();
1431            this.suspendEncoding = true;
1432        } // end suspendEncoding
1433

1434        
1435        /**
1436         * Resumes encoding of the stream.
1437         * May be helpful if you need to embed a piece of
1438         * base640-encoded data in a stream.
1439         *
1440         * @since 1.5.1
1441         */

1442        public void resumeEncoding()
1443        {
1444            this.suspendEncoding = false;
1445        } // end resumeEncoding
1446

1447        
1448        
1449    } // end inner class OutputStream
1450

1451    
1452} // end class Base64
1453
Popular Tags