KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > cowsultants > itracker > ejb > client > util > Base64


1 /*
2  * This software was designed and created by Jason Carroll.
3  * Copyright (c) 2002, 2003, 2004 Jason Carroll.
4  * The author can be reached at jcarroll@cowsultants.com
5  * ITracker website: http://www.cowsultants.com
6  * ITracker forums: http://www.cowsultants.com/phpBB/index.php
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it only under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  * Base64 Original Author: Robert Harder
18  * Base64 Original Author: rob@iharder.net
19  */

20
21 package cowsultants.itracker.ejb.client.util;
22
23 public class Base64 {
24     /** No options specified. Value is zero. */
25     public final static int NO_OPTIONS = 0;
26     /** Specify encoding. */
27     public final static int ENCODE = 1;
28     /** Specify decoding. */
29     public final static int DECODE = 0;
30     /** Specify that data should be gzip-compressed. */
31     public final static int GZIP = 2;
32     /** Don't break lines when encoding (violates strict Base64 specification) */
33     public final static int DONT_BREAK_LINES = 8;
34
35     /** Maximum line length (76) of Base64 output. */
36     private final static int MAX_LINE_LENGTH = 76;
37     /** The equals sign (=) as a byte. */
38     private final static byte EQUALS_SIGN = (byte)'=';
39     /** The new line character (\n) as a byte. */
40     private final static byte NEW_LINE = (byte)'\n';
41     /** Preferred encoding. */
42     private final static String JavaDoc PREFERRED_ENCODING = "UTF-8";
43
44
45     /** The 64 valid Base64 values. */
46     private final static byte[] ALPHABET;
47     private final static byte[] _NATIVE_ALPHABET = {
48         (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
49         (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
50         (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
51         (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
52         (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
53         (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
54         (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
55         (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
56         (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
57         (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
58     };
59
60     /** Determine which ALPHABET to use. */
61     static {
62         byte[] __bytes;
63         try {
64             __bytes = new String JavaDoc("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").getBytes( PREFERRED_ENCODING );
65         } catch (java.io.UnsupportedEncodingException JavaDoc use) {
66             __bytes = _NATIVE_ALPHABET;
67         }
68
69         ALPHABET = __bytes;
70     }
71
72
73     /**
74      * Translates a Base64 value to either its 6-bit reconstruction value
75      * or a negative number indicating some other meaning.
76      **/

77     private final static byte[] DECODABET = {
78         -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
79
-5,-5, // Whitespace: Tab and Linefeed
80
-9,-9, // Decimal 11 - 12
81
-5, // Whitespace: Carriage Return
82
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
83
-9,-9,-9,-9,-9, // Decimal 27 - 31
84
-5, // Whitespace: Space
85
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
86
62, // Plus sign at decimal 43
87
-9,-9,-9, // Decimal 44 - 46
88
63, // Slash at decimal 47
89
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
90
-9,-9,-9, // Decimal 58 - 60
91
-1, // Equals sign at decimal 61
92
-9,-9,-9, // Decimal 62 - 64
93
0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
94
14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
95
-9,-9,-9,-9,-9,-9, // Decimal 91 - 96
96
26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
97
39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
98
-9,-9,-9,-9 // Decimal 123 - 126
99
/*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
100         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
101         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
102         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
103         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
104         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
105         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
106         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
107         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
108         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */

109     };
110
111     private final static byte BAD_ENCODING = -9; // Indicates error in encoding
112
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
113
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
114

115
116     /** Defeats instantiation. */
117     private Base64() {}
118
119     /**
120      * Encodes the first three bytes of array <var>threeBytes</var>
121      * and returns a four-byte array in Base64 notation.
122      *
123      * @param threeBytes the array to convert
124      * @return four byte array in Base64 notation.
125      */

126     private static byte[] encode3to4( byte[] threeBytes ) {
127         return encode3to4( threeBytes, 3 );
128     }
129
130
131
132     /**
133       * Encodes up to the first three bytes of array <var>threeBytes</var>
134       * and returns a four-byte array in Base64 notation.
135       * The actual number of significant bytes in your array is
136       * given by <var>numSigBytes</var>.
137       * The array <var>threeBytes</var> needs only be as big as
138       * <var>numSigBytes</var>.
139       *
140       * @param threeBytes the array to convert
141       * @param numSigBytes the number of significant bytes in your array
142       * @return four byte array in Base64 notation.
143       */

144     private static byte[] encode3to4(byte[] threeBytes, int numSigBytes) {
145         byte[] dest = new byte[4];
146         encode3to4( threeBytes, 0, numSigBytes, dest, 0 );
147         return dest;
148     }
149
150     /**
151       * Encodes up to the first three bytes of array <var>threeBytes</var>
152       * and returns a four-byte array in Base64 notation.
153       * The actual number of significant bytes in your array is
154       * given by <var>numSigBytes</var>.
155       * The array <var>threeBytes</var> needs only be as big as
156       * <var>numSigBytes</var>.
157       * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
158       *
159       * @param b4 A reusable byte array to reduce array instantiation
160       * @param threeBytes the array to convert
161       * @param numSigBytes the number of significant bytes in your array
162       * @return four byte array in Base64 notation.
163       */

164     private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes) {
165         encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
166         return b4;
167     }
168
169     /**
170       * Encodes up to three bytes of the array <var>source</var>
171       * and writes the resulting four Base64 bytes to <var>destination</var>.
172       * The source and destination arrays can be manipulated
173       * anywhere along their length by specifying
174       * <var>srcOffset</var> and <var>destOffset</var>.
175       * This method does not check to make sure your arrays
176       * are large enough to accomodate <var>srcOffset</var> + 3 for
177       * the <var>source</var> array or <var>destOffset</var> + 4 for
178       * the <var>destination</var> array.
179       * The actual number of significant bytes in your array is
180       * given by <var>numSigBytes</var>.
181       *
182       * @param source the array to convert
183       * @param srcOffset the index where conversion begins
184       * @param numSigBytes the number of significant bytes in your array
185       * @param destination the array to hold the conversion
186       * @param destOffset the index where output will be put
187       * @return the <var>destination</var> array
188       */

189     private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset) {
190         // 1 2 3
191
// 01234567890123456789012345678901 Bit position
192
// --------000000001111111122222222 Array position from threeBytes
193
// --------| || || || | Six bit groups to index ALPHABET
194
// >>18 >>12 >> 6 >> 0 Right shift necessary
195
// 0x3f 0x3f 0x3f Additional AND
196

197         // Create buffer with zero-padding if there are only one or two
198
// significant bytes passed in the array.
199
// We have to shift left 24 in order to flush out the 1's that appear
200
// when Java treats a value as negative that is cast from a byte to an int.
201
int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
202                      | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
203                      | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
204
205         switch( numSigBytes ) {
206             case 3:
207                 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
208                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
209                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
210                 destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
211                 return destination;
212
213             case 2:
214                 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
215                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
216                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
217                 destination[ destOffset + 3 ] = EQUALS_SIGN;
218                 return destination;
219
220             case 1:
221                 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
222                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
223                 destination[ destOffset + 2 ] = EQUALS_SIGN;
224                 destination[ destOffset + 3 ] = EQUALS_SIGN;
225                 return destination;
226
227             default:
228                 return destination;
229         }
230     }
231
232     /**
233       * Serializes an object and returns the Base64-encoded
234       * version of that serialized object. If the object
235       * cannot be serialized or there is another error,
236       * the method will return <tt>null</tt>.
237       * The object is not GZip-compressed before being encoded.
238       *
239       * @param serializableObject The object to encode
240       * @return The Base64-encoded object
241       */

242     public static String JavaDoc encodeObject(java.io.Serializable JavaDoc serializableObject) {
243         return encodeObject( serializableObject, NO_OPTIONS );
244     }
245
246     /**
247       * Serializes an object and returns the Base64-encoded
248       * version of that serialized object. If the object
249       * cannot be serialized or there is another error,
250       * the method will return <tt>null</tt>.
251       * <p>
252       * Valid options:<pre>
253       * GZIP: gzip-compresses object before encoding it.
254       * DONT_BREAK_LINES: don't break lines at 76 characters
255       * <i>Note: Technically, this makes your encoding non-compliant.</i>
256       * </pre>
257       * <p>
258       * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
259       * <p>
260       * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
261       *
262       * @param serializableObject The object to encode
263       * @options Specified options
264       * @return The Base64-encoded object
265       * @see Base64#GZIP
266       * @see Base64#DONT_BREAK_LINES
267       */

268     public static String JavaDoc encodeObject(java.io.Serializable JavaDoc serializableObject, int options) {
269         java.io.ByteArrayOutputStream JavaDoc baos = null;
270         java.io.OutputStream JavaDoc b64os = null;
271         java.io.ObjectOutputStream JavaDoc oos = null;
272         java.util.zip.GZIPOutputStream JavaDoc gzos = null;
273
274         int gzip = (options & GZIP);
275         int dontBreakLines = (options & DONT_BREAK_LINES);
276
277         try {
278             // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
279
baos = new java.io.ByteArrayOutputStream JavaDoc();
280             b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
281
282             // GZip?
283
if(gzip == GZIP) {
284                 gzos = new java.util.zip.GZIPOutputStream JavaDoc( b64os );
285                 oos = new java.io.ObjectOutputStream JavaDoc( gzos );
286             } else {
287                 oos = new java.io.ObjectOutputStream JavaDoc( b64os );
288             }
289
290             oos.writeObject( serializableObject );
291         } catch( java.io.IOException JavaDoc e ) {
292             e.printStackTrace();
293             return null;
294         } finally {
295             try{ oos.close(); } catch( Exception JavaDoc e ){}
296             try{ gzos.close(); } catch( Exception JavaDoc e ){}
297             try{ b64os.close(); } catch( Exception JavaDoc e ){}
298             try{ baos.close(); } catch( Exception JavaDoc e ){}
299         }
300
301         // Return value according to relevant encoding.
302
try {
303             return new String JavaDoc( baos.toByteArray(), PREFERRED_ENCODING );
304         } catch (java.io.UnsupportedEncodingException JavaDoc uue) {
305             return new String JavaDoc( baos.toByteArray() );
306         }
307     }
308
309     /**
310       * Encodes a byte array into Base64 notation.
311       * Does not GZip-compress data.
312       *
313       * @param source The data to convert
314       */

315     public static String JavaDoc encodeBytes(byte[] source) {
316         return encodeBytes(source, 0, source.length, NO_OPTIONS);
317     }
318
319     /**
320       * Encodes a byte array into Base64 notation.
321       * <p>
322       * Valid options:<pre>
323       * GZIP: gzip-compresses object before encoding it.
324       * DONT_BREAK_LINES: don't break lines at 76 characters
325       * <i>Note: Technically, this makes your encoding non-compliant.</i>
326       * </pre>
327       * <p>
328       * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
329       * <p>
330       * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
331       *
332       *
333       * @param source The data to convert
334       * @param options Specified options
335       * @see Base64#GZIP
336       * @see Base64#DONT_BREAK_LINES
337       */

338     public static String JavaDoc encodeBytes(byte[] source, int options) {
339         return encodeBytes(source, 0, source.length, options);
340     }
341
342     /**
343       * Encodes a byte array into Base64 notation.
344       * Does not GZip-compress data.
345       *
346       * @param source The data to convert
347       * @param off Offset in array where conversion should begin
348       * @param len Length of data to convert
349       */

350     public static String JavaDoc encodeBytes( byte[] source, int off, int len) {
351         return encodeBytes( source, off, len, NO_OPTIONS );
352     }
353
354     /**
355       * Encodes a byte array into Base64 notation.
356       * <p>
357       * Valid options:<pre>
358       * GZIP: gzip-compresses object before encoding it.
359       * DONT_BREAK_LINES: don't break lines at 76 characters
360       * <i>Note: Technically, this makes your encoding non-compliant.</i>
361       * </pre>
362       * <p>
363       * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
364       * <p>
365       * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
366       *
367       *
368       * @param source The data to convert
369       * @param off Offset in array where conversion should begin
370       * @param len Length of data to convert
371       * @param breakLines Break lines at 80 characters or less.
372       * @param options Specified options
373       * @see Base64#GZIP
374       * @see Base64#DONT_BREAK_LINES
375       */

376     public static String JavaDoc encodeBytes( byte[] source, int off, int len, int options) {
377         // Isolate options
378
int dontBreakLines = ( options & DONT_BREAK_LINES );
379         int gzip = ( options & GZIP );
380
381         // Compress?
382
if(gzip == GZIP) {
383             java.io.ByteArrayOutputStream JavaDoc baos = null;
384             java.util.zip.GZIPOutputStream JavaDoc gzos = null;
385             Base64.OutputStream b64os = null;
386
387             try {
388                 // GZip -> Base64 -> ByteArray
389
baos = new java.io.ByteArrayOutputStream JavaDoc();
390                 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
391                 gzos = new java.util.zip.GZIPOutputStream JavaDoc( b64os );
392
393                 gzos.write( source, off, len );
394                 gzos.close();
395             } catch( java.io.IOException JavaDoc e ) {
396                 e.printStackTrace();
397                 return null;
398             } finally {
399                 try{ gzos.close(); } catch( Exception JavaDoc e ){}
400                 try{ b64os.close(); } catch( Exception JavaDoc e ){}
401                 try{ baos.close(); } catch( Exception JavaDoc e ){}
402             }
403
404             // Return value according to relevant encoding.
405
try {
406                 return new String JavaDoc( baos.toByteArray(), PREFERRED_ENCODING );
407             } catch(java.io.UnsupportedEncodingException JavaDoc uue) {
408                 return new String JavaDoc( baos.toByteArray() );
409             }
410         } else {
411             // Convert option to boolean in way that code likes it.
412
boolean breakLines = dontBreakLines == 0;
413
414             int len43 = len * 4 / 3;
415             byte[] outBuff = new byte[ ( len43 ) // Main 4:3
416
+ ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
417
+ (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
418
int d = 0;
419             int e = 0;
420             int len2 = len - 2;
421             int lineLength = 0;
422             for( ; d < len2; d+=3, e+=4 ) {
423                 encode3to4( source, d+off, 3, outBuff, e );
424
425                 lineLength += 4;
426                 if( breakLines && lineLength == MAX_LINE_LENGTH ) {
427                     outBuff[e+4] = NEW_LINE;
428                     e++;
429                     lineLength = 0;
430                 }
431             }
432
433             if( d < len ) {
434                 encode3to4( source, d+off, len - d, outBuff, e );
435                 e += 4;
436             }
437
438             // Return value according to relevant encoding.
439
try {
440                 return new String JavaDoc( outBuff, 0, e, PREFERRED_ENCODING );
441             } catch (java.io.UnsupportedEncodingException JavaDoc uue) {
442                 return new String JavaDoc( outBuff, 0, e );
443             }
444         }
445     }
446
447     /**
448      * Decodes the first four bytes of array <var>fourBytes</var>
449      * and returns an array up to three bytes long with the
450      * decoded values.
451      *
452      * @param fourBytes the array with Base64 content
453      * @return array with decoded values
454      */

455     private static byte[] decode4to3( byte[] fourBytes ) {
456         byte[] outBuff1 = new byte[3];
457         int count = decode4to3( fourBytes, 0, outBuff1, 0 );
458         byte[] outBuff2 = new byte[ count ];
459
460         for( int i = 0; i < count; i++ )
461             outBuff2[i] = outBuff1[i];
462
463         return outBuff2;
464     }
465
466     /**
467      * Decodes four bytes from array <var>source</var>
468      * and writes the resulting bytes (up to three of them)
469      * to <var>destination</var>.
470      * The source and destination arrays can be manipulated
471      * anywhere along their length by specifying
472      * <var>srcOffset</var> and <var>destOffset</var>.
473      * This method does not check to make sure your arrays
474      * are large enough to accomodate <var>srcOffset</var> + 4 for
475      * the <var>source</var> array or <var>destOffset</var> + 3 for
476      * the <var>destination</var> array.
477      * This method returns the actual number of bytes that
478      * were converted from the Base64 encoding.
479      *
480      *
481      * @param source the array to convert
482      * @param srcOffset the index where conversion begins
483      * @param destination the array to hold the conversion
484      * @param destOffset the index where output will be put
485      * @return the number of decoded bytes converted
486      */

487     private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) {
488         // Example: Dk==
489
if( source[ srcOffset + 2] == EQUALS_SIGN )
490         {
491             // Two ways to do the same thing. Don't know which way I like best.
492
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
493
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
494
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
495                           | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
496
497             destination[ destOffset ] = (byte)( outBuff >>> 16 );
498             return 1;
499         } else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) {
500             // Two ways to do the same thing. Don't know which way I like best.
501
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
502
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
503
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
504
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
505                           | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
506                           | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
507
508             destination[ destOffset ] = (byte)( outBuff >>> 16 );
509             destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
510             return 2;
511         } else {
512             try {
513                 // Two ways to do the same thing. Don't know which way I like best.
514
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
515
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
516
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
517
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
518
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
519                               | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
520                               | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
521                               | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
522
523
524                 destination[ destOffset ] = (byte)( outBuff >> 16 );
525                 destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
526                 destination[ destOffset + 2 ] = (byte)( outBuff );
527
528                 return 3;
529             }catch( Exception JavaDoc e){
530                 System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) );
531                 System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) );
532                 System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) );
533                 System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) );
534                 return -1;
535             }
536         }
537     }
538
539     /**
540      * Very low-level access to decoding ASCII characters in
541      * the form of a byte array. Does not support automatically
542      * gunzipping or any other "fancy" features.
543      *
544      * @param source The Base64 encoded data
545      * @param off The offset of where to begin decoding
546      * @param len The length of characters to decode
547      * @return decoded data
548      */

549     public static byte[] decode( byte[] source, int off, int len ) {
550         int len34 = len * 3 / 4;
551         byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
552
int outBuffPosn = 0;
553
554         byte[] b4 = new byte[4];
555         int b4Posn = 0;
556         int i = 0;
557         byte sbiCrop = 0;
558         byte sbiDecode = 0;
559         for( i = off; i < off+len; i++ )
560         {
561             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
562
sbiDecode = DECODABET[ sbiCrop ];
563
564             if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
565
{
566                 if( sbiDecode >= EQUALS_SIGN_ENC )
567                 {
568                     b4[ b4Posn++ ] = sbiCrop;
569                     if( b4Posn > 3 )
570                     {
571                         outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
572                         b4Posn = 0;
573
574                         // If that was the equals sign, break out of 'for' loop
575
if( sbiCrop == EQUALS_SIGN )
576                             break;
577                     } // end if: quartet built
578

579                 } // end if: equals sign or better
580

581             } else {
582                 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
583                 return null;
584             } // end else:
585
} // each input character
586

587         byte[] out = new byte[ outBuffPosn ];
588         System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
589         return out;
590     }
591
592     /**
593      * Decodes data from Base64 notation, automatically
594      * detecting gzip-compressed data and decompressing it.
595      *
596      * @param s the string to decode
597      * @return the decoded data
598      */

599     public static byte[] decode( String JavaDoc s )
600     {
601         byte[] bytes;
602         try {
603             bytes = s.getBytes( PREFERRED_ENCODING );
604         } catch( java.io.UnsupportedEncodingException JavaDoc uee ) {
605             bytes = s.getBytes();
606         }
607
608         // Decode
609
bytes = decode( bytes, 0, bytes.length );
610
611         // Check to see if it's gzip-compressed
612
// GZIP Magic Two-Byte Number: 0x8b1f (35615)
613
if( bytes.length >= 2 )
614         {
615
616             int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
617             if( bytes != null && bytes.length >= 4 && java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
618                 java.io.ByteArrayInputStream JavaDoc bais = null;
619                 java.util.zip.GZIPInputStream JavaDoc gzis = null;
620                 java.io.ByteArrayOutputStream JavaDoc baos = null;
621                 byte[] buffer = new byte[2048];
622                 int length = 0;
623
624                 try
625                 {
626                     baos = new java.io.ByteArrayOutputStream JavaDoc();
627                     bais = new java.io.ByteArrayInputStream JavaDoc( bytes );
628                     gzis = new java.util.zip.GZIPInputStream JavaDoc( bais );
629
630                     while( ( length = gzis.read( buffer ) ) >= 0 )
631                     {
632                         baos.write(buffer,0,length);
633                     } // end while: reading input
634

635                     // No error? Get new bytes.
636
bytes = baos.toByteArray();
637
638                 } catch( java.io.IOException JavaDoc e ) {
639                 } finally {
640                     try{ baos.close(); } catch( Exception JavaDoc e ){}
641                     try{ gzis.close(); } catch( Exception JavaDoc e ){}
642                     try{ bais.close(); } catch( Exception JavaDoc e ){}
643                 }
644             }
645         }
646
647         return bytes;
648     }
649
650
651
652     /**
653      * Attempts to decode Base64 data and deserialize a Java
654      * Object within. Returns <tt>null</tt> if there was an error.
655      *
656      * @param encodedObject The Base64 data to decode
657      * @return The decoded and deserialized object
658      */

659     public static Object JavaDoc decodeToObject( String JavaDoc encodedObject ) {
660         // Decode and gunzip if necessary
661
byte[] objBytes = decode( encodedObject );
662
663         java.io.ByteArrayInputStream JavaDoc bais = null;
664         java.io.ObjectInputStream JavaDoc ois = null;
665         Object JavaDoc obj = null;
666
667         try
668         {
669             bais = new java.io.ByteArrayInputStream JavaDoc( objBytes );
670             ois = new java.io.ObjectInputStream JavaDoc( bais );
671
672             obj = ois.readObject();
673         } catch( java.io.IOException JavaDoc e ) {
674             e.printStackTrace();
675             obj = null;
676         } catch( java.lang.ClassNotFoundException JavaDoc e ) {
677             e.printStackTrace();
678             obj = null;
679         } finally {
680             try{ bais.close(); } catch( Exception JavaDoc e ){}
681             try{ ois.close(); } catch( Exception JavaDoc e ){}
682         }
683
684         return obj;
685     }
686
687     /**
688      * A {@link Base64#InputStream} will read data from another
689      * {@link java.io.InputStream}, given in the constructor,
690      * and encode/decode to/from Base64 notation on the fly.
691      *
692      * @see Base64
693      * @see java.io.FilterInputStream
694      */

695     public static class InputStream extends java.io.FilterInputStream JavaDoc
696     {
697         private int options; // Options specified
698
private boolean encode; // Encoding or decoding
699
private int position; // Current position in the buffer
700
private byte[] buffer; // Small buffer holding converted data
701
private int bufferLength; // Length of buffer (3 or 4)
702
private int numSigBytes; // Number of meaningful bytes in the buffer
703
private int lineLength;
704         private boolean breakLines; // Break lines at less than 80 characters
705

706
707         /**
708          * Constructs a {@link Base64#InputStream} in DECODE mode.
709          *
710          * @param in the {@link java.io.InputStream} from which to read data.
711          */

712         public InputStream( java.io.InputStream JavaDoc in )
713         {
714             this( in, DECODE );
715         }
716
717
718         /**
719          * Constructs a {@link Base64#InputStream} in
720          * either ENCODE or DECODE mode.
721          * <p>
722          * Valid options:<pre>
723          * ENCODE or DECODE: Encode or Decode as data is read.
724          * DONT_BREAK_LINES: don't break lines at 76 characters
725          * (only meaningful when encoding)
726          * <i>Note: Technically, this makes your encoding non-compliant.</i>
727          * </pre>
728          * <p>
729          * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
730          *
731          *
732          * @param in the {@link java.io.InputStream} from which to read data.
733          * @param options Specified options
734          * @see Base64#ENCODE
735          * @see Base64#DECODE
736          * @see Base64#DONT_BREAK_LINES
737          */

738         public InputStream( java.io.InputStream JavaDoc in, int options )
739         {
740             super( in );
741             this.options = options;
742             this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
743             this.encode = (options & ENCODE) == ENCODE;
744             this.breakLines = breakLines;
745             this.encode = encode;
746             this.bufferLength = encode ? 4 : 3;
747             this.buffer = new byte[ bufferLength ];
748             this.position = -1;
749             this.lineLength = 0;
750         }
751
752         /**
753          * Reads enough of the input stream to convert
754          * to/from Base64 and returns the next byte.
755          *
756          * @return next byte
757          */

758         public int read() throws java.io.IOException JavaDoc {
759             // Do we need to get data?
760
if( position < 0 ) {
761                 if( encode ) {
762                     byte[] b3 = new byte[3];
763                     int numBinaryBytes = 0;
764                     for( int i = 0; i < 3; i++ ) {
765                         try {
766                             int b = in.read();
767
768                             // If end of stream, b is -1.
769
if( b >= 0 ) {
770                                 b3[i] = (byte)b;
771                                 numBinaryBytes++;
772                             }
773                         } catch( java.io.IOException JavaDoc e ) {
774                             // Only a problem if we got no data at all.
775
if( i == 0 )
776                                 throw e;
777
778                         }
779                     }
780
781                     if( numBinaryBytes > 0 )
782                     {
783                         encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
784                         position = 0;
785                         numSigBytes = 4;
786                     } else {
787                         return -1;
788                     }
789                 } else {
790                     byte[] b4 = new byte[4];
791                     int i = 0;
792                     for( i = 0; i < 4; i++ )
793                     {
794                         // Read four "meaningful" bytes:
795
int b = 0;
796                         do{ b = in.read(); }
797                         while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
798
799                         if( b < 0 )
800                             break; // Reads a -1 if end of stream
801

802                         b4[i] = (byte)b;
803                     } // end for: each needed input byte
804

805                     if( i == 4 )
806                     {
807                         numSigBytes = decode4to3( b4, 0, buffer, 0 );
808                         position = 0;
809                     } else if( i == 0 ) {
810                         return -1;
811                     } else {
812                         // Must have broken out from above.
813
throw new java.io.IOException JavaDoc( "Improperly padded Base64 input." );
814                     }
815                 }
816             }
817
818             // Got data?
819
if( position >= 0 ) {
820                 // End of relevant data?
821
if( /*!encode &&*/ position >= numSigBytes )
822                     return -1;
823
824                 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) {
825                     lineLength = 0;
826                     return '\n';
827                 } else {
828                     lineLength++; // This isn't important when decoding
829
// but throwing an extra "if" seems
830
// just as wasteful.
831

832                     int b = buffer[ position++ ];
833
834                     if( position >= bufferLength )
835                         position = -1;
836
837                     return b & 0xFF; // This is how you "cast" a byte that's
838
// intended to be unsigned.
839
} // end else
840
} else {
841                 // When JDK1.4 is more accepted, use an assertion here.
842
throw new java.io.IOException JavaDoc( "Error in Base64 code reading stream." );
843             } // end else
844
} // end read
845

846
847         /**
848          * Calls {@link #read} repeatedly until the end of stream
849          * is reached or <var>len</var> bytes are read.
850          * Returns number of bytes read into array or -1 if
851          * end of stream is encountered.
852          *
853          * @param dest array to hold values
854          * @param off offset for array
855          * @param len max number of bytes to read into array
856          * @return bytes read into array or -1 if end of stream is encountered.
857          */

858         public int read( byte[] dest, int off, int len ) throws java.io.IOException JavaDoc
859         {
860             int i;
861             int b;
862             for( i = 0; i < len; i++ )
863             {
864                 b = read();
865
866                 //if( b < 0 && i == 0 )
867
// return -1;
868

869                 if( b >= 0 )
870                     dest[off + i] = (byte)b;
871                 else if( i == 0 )
872                     return -1;
873                 else
874                     break; // Out of 'for' loop
875
} // end for: each byte read
876
return i;
877         } // end read
878

879     } // end inner class InputStream
880

881
882     /**
883      * A {@link Base64#OutputStream} will write data to another
884      * {@link java.io.OutputStream}, given in the constructor,
885      * and encode/decode to/from Base64 notation on the fly.
886      *
887      * @see Base64
888      * @see java.io.FilterOutputStream
889      */

890     public static class OutputStream extends java.io.FilterOutputStream JavaDoc {
891         private int options;
892         private boolean encode;
893         private int position;
894         private byte[] buffer;
895         private int bufferLength;
896         private int lineLength;
897         private boolean breakLines;
898         private byte[] b4; // Scratch used in a few places
899
private boolean suspendEncoding;
900
901         /**
902          * Constructs a {@link Base64#OutputStream} in ENCODE mode.
903          *
904          * @param out the {@link java.io.OutputStream} to which data will be written.
905          * @since 1.3
906          */

907         public OutputStream( java.io.OutputStream JavaDoc out )
908         {
909             this( out, ENCODE );
910         } // end constructor
911

912
913         /**
914          * Constructs a {@link Base64#OutputStream} in
915          * either ENCODE or DECODE mode.
916          * <p>
917          * Valid options:<pre>
918          * ENCODE or DECODE: Encode or Decode as data is read.
919          * DONT_BREAK_LINES: don't break lines at 76 characters
920          * (only meaningful when encoding)
921          * <i>Note: Technically, this makes your encoding non-compliant.</i>
922          * </pre>
923          * <p>
924          * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
925          *
926          * @param out the {@link java.io.OutputStream} to which data will be written.
927          * @param options Specified options.
928          * @see Base64#ENCODE
929          * @see Base64#DECODE
930          * @see Base64#DONT_BREAK_LINES
931          */

932         public OutputStream( java.io.OutputStream JavaDoc out, int options )
933         {
934             super( out );
935             this.options = options;
936             this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
937             this.encode = (options & ENCODE) == ENCODE;
938             this.bufferLength = encode ? 3 : 4;
939             this.buffer = new byte[ bufferLength ];
940             this.position = 0;
941             this.lineLength = 0;
942             this.suspendEncoding = false;
943             this.b4 = new byte[4];
944         }
945
946         /**
947          * Writes the byte to the output stream after
948          * converting to/from Base64 notation.
949          * When encoding, bytes are buffered three
950          * at a time before the output stream actually
951          * gets a write() call.
952          * When decoding, bytes are buffered four
953          * at a time.
954          *
955          * @param theByte the byte to write
956          */

957         public void write(int theByte) throws java.io.IOException JavaDoc {
958             // Encoding suspended?
959
if( suspendEncoding )
960             {
961                 super.out.write( theByte );
962                 return;
963             } // end if: supsended
964

965             if( encode ) {
966                 buffer[ position++ ] = (byte)theByte;
967                 if( position >= bufferLength ) // Enough to encode.
968
{
969                     out.write( encode3to4( b4, buffer, bufferLength ) );
970
971                     lineLength += 4;
972                     if( breakLines && lineLength >= MAX_LINE_LENGTH )
973                     {
974                         out.write( NEW_LINE );
975                         lineLength = 0;
976                     } // end if: end of line
977

978                     position = 0;
979                 } // end if: enough to output
980
} else {
981                 // Meaningful Base64 character?
982
if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
983                 {
984                     buffer[ position++ ] = (byte)theByte;
985                     if( position >= bufferLength ) // Enough to output.
986
{
987                         int len = Base64.decode4to3( buffer, 0, b4, 0 );
988                         out.write( b4, 0, len );
989                         //out.write( Base64.decode4to3( buffer ) );
990
position = 0;
991                     } // end if: enough to output
992
} else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) {
993                     throw new java.io.IOException JavaDoc( "Invalid character in Base64 data." );
994                 } // end else: not white space either
995
} // end else: decoding
996
} // end write
997

998
999
1000        /**
1001         * Calls {@link #write} repeatedly until <var>len</var>
1002         * bytes are written.
1003         *
1004         * @param theBytes array from which to read bytes
1005         * @param off offset for array
1006         * @param len max number of bytes to read into array
1007         */

1008        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException JavaDoc {
1009            // Encoding suspended?
1010
if( suspendEncoding ) {
1011                super.out.write( theBytes, off, len );
1012                return;
1013            }
1014
1015            for( int i = 0; i < len; i++ ) {
1016                write( theBytes[ off + i ] );
1017            }
1018
1019        }
1020
1021        /**
1022         * This pads the buffer without closing the stream.
1023         */

1024        public void flushBase64() throws java.io.IOException JavaDoc {
1025            if( position > 0 ) {
1026                if( encode ) {
1027                    out.write( encode3to4( b4, buffer, position ) );
1028                    position = 0;
1029                } else {
1030                    throw new java.io.IOException JavaDoc( "Base64 input not properly padded." );
1031                }
1032            }
1033        }
1034
1035        /**
1036         * Flushes and closes (I think, in the superclass) the stream.
1037         */

1038        public void close() throws java.io.IOException JavaDoc {
1039            // 1. Ensure that pending characters are written
1040
flushBase64();
1041
1042            // 2. Actually close the stream
1043
// Base class both flushes and closes.
1044
super.close();
1045
1046            buffer = null;
1047            out = null;
1048        }
1049
1050        /**
1051         * Suspends encoding of the stream.
1052         * May be helpful if you need to embed a piece of
1053         * base640-encoded data in a stream.
1054         */

1055        public void suspendEncoding() throws java.io.IOException JavaDoc {
1056            flushBase64();
1057            this.suspendEncoding = true;
1058        }
1059
1060        /**
1061         * Resumes encoding of the stream.
1062         * May be helpful if you need to embed a piece of
1063         * base640-encoded data in a stream.
1064         */

1065        public void resumeEncoding() {
1066            this.suspendEncoding = false;
1067        }
1068    }
1069
1070}
1071
Popular Tags