Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/embl/ebi/escience/baclava/Base64.java


1   package org.embl.ebi.escience.baclava;
2   
3   /**
4    * Encodes and decodes to and from Base64 notation.
5    *
6    * <p>
7    * Change Log:
8    * </p>
9    * <ul>
10   *  <li>v2.0 - I got rid of methods that used booleans to set options. 
11   *   Now everything is more consolidated and cleaner. The code now detects
12   *   when data that's being decoded is gzip-compressed and will decompress it
13   *   automatically. Generally things are cleaner. You'll probably have to
14   *   change some method calls that you were making to support the new
15   *   options format (<tt>int</tt>s that you "OR" together).</li>
16   *  <li>v1.5.1 - Fixed bug when decompressing and decoding to a             
17   *   byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.      
18   *   Added the ability to "suspend" encoding in the Output Stream so        
19   *   you can turn on and off the encoding if you need to embed base64       
20   *   data in an otherwise "normal" stream (like an XML file).</li>  
21   *  <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
22   *      This helps when using GZIP streams.
23   *      Added the ability to GZip-compress objects before encoding them.</li>
24   *  <li>v1.4 - Added helper methods to read/write files.</li>
25   *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
26   *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
27   *      where last buffer being read, if not completely full, was not returned.</li>
28   *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
29   *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
30   * </ul>
31   *
32   * <p>
33   * I am placing this code in the Public Domain. Do with it as you will.
34   * This software comes with no guarantees or warranties but with
35   * plenty of well-wishing instead!
36   * Please visit <a href="http://iharder.net/xmlizable">http://iharder.net/base64</a>
37   * periodically to check for updates or to contribute improvements.
38   * </p>
39   *
40   * @author Robert Harder
41   * @author rob@iharder.net
42   * @version 2.0
43   */
44  public class Base64
45  {
46      
47  /* ********  P U B L I C   F I E L D S  ******** */   
48      
49      
50      /** No options specified. Value is zero. */
51      public final static int NO_OPTIONS = 0;
52      
53      /** Specify encoding. */
54      public final static int ENCODE = 1;
55      
56      
57      /** Specify decoding. */
58      public final static int DECODE = 0;
59      
60      
61      /** Specify that data should be gzip-compressed. */
62      public final static int GZIP = 2;
63      
64      
65      /** Don't break lines when encoding (violates strict Base64 specification) */
66      public final static int DONT_BREAK_LINES = 8;
67      
68      
69  /* ********  P R I V A T E   F I E L D S  ******** */  
70      
71      
72      /** Maximum line length (76) of Base64 output. */
73      private final static int MAX_LINE_LENGTH = 76;
74      
75      
76      /** The equals sign (=) as a byte. */
77      private final static byte EQUALS_SIGN = (byte)'=';
78      
79      
80      /** The new line character (\n) as a byte. */
81      private final static byte NEW_LINE = (byte)'\n';
82      
83      
84      /** The 64 valid Base64 values. */
85      private final static byte[] ALPHABET =
86      {
87          (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
88          (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
89          (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
90          (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
91          (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
92          (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
93          (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
94          (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
95          (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
96          (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
97      };
98      
99      /** 
100      * Translates a Base64 value to either its 6-bit reconstruction value
101      * or a negative number indicating some other meaning.
102      **/
103     private final static byte[] DECODABET =
104     {   
105         -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
106         -5,-5,                                      // Whitespace: Tab and Linefeed
107         -9,-9,                                      // Decimal 11 - 12
108         -5,                                         // Whitespace: Carriage Return
109         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
110         -9,-9,-9,-9,-9,                             // Decimal 27 - 31
111         -5,                                         // Whitespace: Space
112         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
113         62,                                         // Plus sign at decimal 43
114         -9,-9,-9,                                   // Decimal 44 - 46
115         63,                                         // Slash at decimal 47
116         52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
117         -9,-9,-9,                                   // Decimal 58 - 60
118         -1,                                         // Equals sign at decimal 61
119         -9,-9,-9,                                      // Decimal 62 - 64
120         0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
121         14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
122         -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
123         26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
124         39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
125         -9,-9,-9,-9                                 // Decimal 123 - 126
126         /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
127         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
128         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
129         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
130         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
131         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
132         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
133         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
134         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
135         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
136     };
137     
138     private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
139     private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
140     private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
141 
142     
143     /** Defeats instantiation. */
144     private Base64(){}
145     
146     
147     
148 /* ********  E N C O D I N G   M E T H O D S  ******** */    
149     
150     
151     /**
152      * Encodes the first three bytes of array <var>threeBytes</var>
153      * and returns a four-byte array in Base64 notation.
154      *
155      * @param threeBytes the array to convert
156      * @return four byte array in Base64 notation.
157      * @since 1.3
158      */
159     private static byte[] encode3to4( byte[] threeBytes )
160     {   
161         return encode3to4( threeBytes, 3 );
162     }   // end encodeToBytes
163     
164     
165     
166     /**
167      * Encodes up to the first three bytes of array <var>threeBytes</var>
168      * and returns a four-byte array in Base64 notation.
169      * The actual number of significant bytes in your array is
170      * given by <var>numSigBytes</var>.
171      * The array <var>threeBytes</var> needs only be as big as
172      * <var>numSigBytes</var>.
173      *
174      * @param threeBytes the array to convert
175      * @param numSigBytes the number of significant bytes in your array
176      * @return four byte array in Base64 notation.
177      * @since 1.3
178      */
179     private static byte[] encode3to4( byte[] threeBytes, int numSigBytes )
180     {  
181         byte[] dest = new byte[4];
182         encode3to4( threeBytes, 0, numSigBytes, dest, 0 );
183         return dest;
184     }
185     
186      /**
187      * Encodes up to the first three bytes of array <var>threeBytes</var>
188      * and returns a four-byte array in Base64 notation.
189      * The actual number of significant bytes in your array is
190      * given by <var>numSigBytes</var>.
191      * The array <var>threeBytes</var> needs only be as big as
192      * <var>numSigBytes</var>.
193      * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
194      *
195      * @param b4 A reusable byte array to reduce array instantiation
196      * @param threeBytes the array to convert
197      * @param numSigBytes the number of significant bytes in your array
198      * @return four byte array in Base64 notation.
199      * @since 1.5.1
200      */
201     private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes )
202     {
203         encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
204         return b4;
205     }   // end encode3to4
206 
207     
208     /**
209      * Encodes up to three bytes of the array <var>source</var>
210      * and writes the resulting four Base64 bytes to <var>destination</var>.
211      * The source and destination arrays can be manipulated
212      * anywhere along their length by specifying 
213      * <var>srcOffset</var> and <var>destOffset</var>.
214      * This method does not check to make sure your arrays
215      * are large enough to accomodate <var>srcOffset</var> + 3 for
216      * the <var>source</var> array or <var>destOffset</var> + 4 for
217      * the <var>destination</var> array.
218      * The actual number of significant bytes in your array is
219      * given by <var>numSigBytes</var>.
220      *
221      * @param source the array to convert
222      * @param srcOffset the index where conversion begins
223      * @param numSigBytes the number of significant bytes in your array
224      * @param destination the array to hold the conversion
225      * @param destOffset the index where output will be put
226      * @return the <var>destination</var> array
227      * @since 1.3
228      */
229     private static byte[] encode3to4( 
230      byte[] source, int srcOffset, int numSigBytes,
231      byte[] destination, int destOffset )
232     {
233         //           1         2         3  
234         // 01234567890123456789012345678901 Bit position
235         // --------000000001111111122222222 Array position from threeBytes
236         // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
237         //          >>18  >>12  >> 6  >> 0  Right shift necessary
238         //                0x3f  0x3f  0x3f  Additional AND
239         
240         // Create buffer with zero-padding if there are only one or two
241         // significant bytes passed in the array.
242         // We have to shift left 24 in order to flush out the 1's that appear
243         // when Java treats a value as negative that is cast from a byte to an int.
244         int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
245                      | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
246                      | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
247 
248         switch( numSigBytes )
249         {
250             case 3:
251                 destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
252                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
253                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
254                 destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
255                 return destination;
256                 
257             case 2:
258                 destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
259                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
260                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
261                 destination[ destOffset + 3 ] = EQUALS_SIGN;
262                 return destination;
263                 
264             case 1:
265                 destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
266                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
267                 destination[ destOffset + 2 ] = EQUALS_SIGN;
268                 destination[ destOffset + 3 ] = EQUALS_SIGN;
269                 return destination;
270                 
271             default:
272                 return destination;
273         }   // end switch
274     }   // end encode3to4
275     
276     
277     
278     /**
279      * Serializes an object and returns the Base64-encoded
280      * version of that serialized object. If the object
281      * cannot be serialized or there is another error,
282      * the method will return <tt>null</tt>.
283      * The object is not GZip-compressed before being encoded.
284      *
285      * @param serializableObject The object to encode
286      * @return The Base64-encoded object
287      * @since 1.4
288      */
289     public static String encodeObject( java.io.Serializable serializableObject )
290     {
291         return encodeObject( serializableObject, NO_OPTIONS );
292     }   // end encodeObject
293     
294 
295 
296     /**
297      * Serializes an object and returns the Base64-encoded
298      * version of that serialized object. If the object
299      * cannot be serialized or there is another error,
300      * the method will return <tt>null</tt>.
301      * <p>
302      * Valid options:<pre>
303      *   GZIP: gzip-compresses object before encoding it.
304      *   DONT_BREAK_LINES: don't break lines at 76 characters
305      *     <i>Note: Technically, this makes your encoding non-compliant.</i>
306      * </pre>
307      * <p>
308      * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
309      * <p>
310      * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
311      *
312      * @param serializableObject The object to encode
313      * @options Specified options
314      * @return The Base64-encoded object
315      * @see Base64#GZIP
316      * @see Base64#DONT_BREAK_LINES
317      * @since 2.0
318      */
319     public static String encodeObject( java.io.Serializable serializableObject, int options )
320     {
321         // Streams
322         java.io.ByteArrayOutputStream  baos  = null; 
323         java.io.OutputStream           b64os = null; 
324         java.io.ObjectOutputStream     oos   = null; 
325         java.util.zip.GZIPOutputStream gzos  = null;
326         
327         // Isolate options
328         int gzip           = (options & GZIP);
329         int dontBreakLines = (options & DONT_BREAK_LINES);
330         
331         try
332         {
333             // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
334             baos  = new java.io.ByteArrayOutputStream();
335             b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
336     
337             // GZip?
338             if( gzip == GZIP )
339             {
340                 gzos = new java.util.zip.GZIPOutputStream( b64os );
341                 oos  = new java.io.ObjectOutputStream( gzos );
342             }   // end if: gzip
343             else
344                 oos   = new java.io.ObjectOutputStream( b64os );
345             
346             oos.writeObject( serializableObject );
347         }   // end try
348         catch( java.io.IOException e )
349         {
350             e.printStackTrace();
351             return null;
352         }   // end catch
353         finally
354         {
355             try{ oos.close();   } catch( Exception e ){}
356             try{ gzos.close();  } catch( Exception e ){}
357             try{ b64os.close(); } catch( Exception e ){}
358             try{ baos.close();  } catch( Exception e ){}
359         }   // end finally
360         
361         return new String( baos.toByteArray() );
362     }   // end encode
363     
364     
365 
366     /**
367      * Encodes a byte array into Base64 notation.
368      * Does not GZip-compress data.
369      *
370      * @param source The data to convert
371      * @since 1.4
372      */
373     public static String encodeBytes( byte[] source )
374     {
375         return encodeBytes( source, 0, source.length, NO_OPTIONS );
376     }   // end encodeBytes
377     
378 
379 
380     /**
381      * Encodes a byte array into Base64 notation.
382      * <p>
383      * Valid options:<pre>
384      *   GZIP: gzip-compresses object before encoding it.
385      *   DONT_BREAK_LINES: don't break lines at 76 characters
386      *     <i>Note: Technically, this makes your encoding non-compliant.</i>
387      * </pre>
388      * <p>
389      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
390      * <p>
391      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
392      *
393      *
394      * @param source The data to convert
395      * @param options Specified options
396      * @see Base64#GZIP
397      * @see Base64#DONT_BREAK_LINES
398      * @since 2.0
399      */
400     public static String encodeBytes( byte[] source, int options )
401     {   
402         return encodeBytes( source, 0, source.length, options );
403     }   // end encodeBytes
404     
405     
406     /**
407      * Encodes a byte array into Base64 notation.
408      * Does not GZip-compress data.
409      *
410      * @param source The data to convert
411      * @param off Offset in array where conversion should begin
412      * @param len Length of data to convert
413      * @since 1.4
414      */
415     public static String encodeBytes( byte[] source, int off, int len )
416     {
417         return encodeBytes( source, off, len, NO_OPTIONS );
418     }   // end encodeBytes
419     
420     
421 
422     /**
423      * Encodes a byte array into Base64 notation.
424      * <p>
425      * Valid options:<pre>
426      *   GZIP: gzip-compresses object before encoding it.
427      *   DONT_BREAK_LINES: don't break lines at 76 characters
428      *     <i>Note: Technically, this makes your encoding non-compliant.</i>
429      * </pre>
430      * <p>
431      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
432      * <p>
433      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
434      *
435      *
436      * @param source The data to convert
437      * @param off Offset in array where conversion should begin
438      * @param len Length of data to convert
439      * @param breakLines Break lines at 80 characters or less.
440      * @param options Specified options
441      * @see Base64#GZIP
442      * @see Base64#DONT_BREAK_LINES
443      * @since 2.0
444      */
445     public static String encodeBytes( byte[] source, int off, int len, int options )
446     {
447         // Isolate options
448         int dontBreakLines = ( options & DONT_BREAK_LINES );
449         int gzip           = ( options & GZIP   );
450         
451         // Compress?
452         if( gzip == GZIP )
453         {
454             java.io.ByteArrayOutputStream  baos  = null;
455             java.util.zip.GZIPOutputStream gzos  = null;
456             Base64.OutputStream            b64os = null;
457             
458     
459             try
460             {
461                 // GZip -> Base64 -> ByteArray
462                 baos = new java.io.ByteArrayOutputStream();
463                 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
464                 gzos  = new java.util.zip.GZIPOutputStream( b64os ); 
465             
466                 gzos.write( source, off, len );
467                 gzos.close();
468             }   // end try
469             catch( java.io.IOException e )
470             {
471                 e.printStackTrace();
472                 return null;
473             }   // end catch
474             finally
475             {
476                 try{ gzos.close();  } catch( Exception e ){}
477                 try{ b64os.close(); } catch( Exception e ){}
478                 try{ baos.close();  } catch( Exception e ){}
479             }   // end finally
480 
481             return new String( baos.toByteArray() );
482         }   // end if: compress
483         
484         // Else, don't compress. Better not to use streams at all then.
485         else
486         {
487             // Convert option to boolean in way that code likes it.
488             boolean breakLines = dontBreakLines == 0;
489             
490             int    len43   = len * 4 / 3;
491             byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
492                                        + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
493                                        + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines      
494             int d = 0;
495             int e = 0;
496             int len2 = len - 2;
497             int lineLength = 0;
498             for( ; d < len2; d+=3, e+=4 )
499             {
500                 encode3to4( source, d+off, 3, outBuff, e );
501 
502                 lineLength += 4;
503                 if( breakLines && lineLength == MAX_LINE_LENGTH )
504                 {   
505                     outBuff[e+4] = NEW_LINE;
506                     e++;
507                     lineLength = 0;
508                 }   // end if: end of line
509             }   // en dfor: each piece of array
510 
511             if( d < len )
512             {
513                 encode3to4( source, d+off, len - d, outBuff, e );
514                 e += 4;
515             }   // end if: some padding needed
516 
517             return new String( outBuff, 0, e );
518         }   // end else: don't compress
519         
520     }   // end encodeBytes
521     
522 
523     
524     
525     
526 /* ********  D E C O D I N G   M E T H O D S  ******** */
527     
528     
529     /**
530      * Decodes the first four bytes of array <var>fourBytes</var>
531      * and returns an array up to three bytes long with the
532      * decoded values.
533      *
534      * @param fourBytes the array with Base64 content
535      * @return array with decoded values
536      * @since 1.3
537      */
538     private static byte[] decode4to3( byte[] fourBytes )
539     {
540         byte[] outBuff1 = new byte[3];
541         int    count    = decode4to3( fourBytes, 0, outBuff1, 0 );
542         byte[] outBuff2 = new byte[ count ];
543         
544         for( int i = 0; i < count; i++ )
545             outBuff2[i] = outBuff1[i];
546         
547         return outBuff2;
548     }
549     
550     
551     
552     
553     /**
554      * Decodes four bytes from array <var>source</var>
555      * and writes the resulting bytes (up to three of them)
556      * to <var>destination</var>.
557      * The source and destination arrays can be manipulated
558      * anywhere along their length by specifying 
559      * <var>srcOffset</var> and <var>destOffset</var>.
560      * This method does not check to make sure your arrays
561      * are large enough to accomodate <var>srcOffset</var> + 4 for
562      * the <var>source</var> array or <var>destOffset</var> + 3 for
563      * the <var>destination</var> array.
564      * This method returns the actual number of bytes that 
565      * were converted from the Base64 encoding.
566      * 
567      *
568      * @param source the array to convert
569      * @param srcOffset the index where conversion begins
570      * @param destination the array to hold the conversion
571      * @param destOffset the index where output will be put
572      * @return the number of decoded bytes converted
573      * @since 1.3
574      */
575     private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
576     {
577         // Example: Dk==
578         if( source[ srcOffset + 2] == EQUALS_SIGN )
579         {
580             // Two ways to do the same thing. Don't know which way I like best.
581             //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
582             //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
583             int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
584                           | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
585             
586             destination[ destOffset ] = (byte)( outBuff >>> 16 );
587             return 1;
588         }
589         
590         // Example: DkL=
591         else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
592         {
593             // Two ways to do the same thing. Don't know which way I like best.
594             //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
595             //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
596             //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
597             int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
598                           | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
599                           | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
600             
601             destination[ destOffset     ] = (byte)( outBuff >>> 16 );
602             destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
603             return 2;
604         }
605         
606         // Example: DkLE
607         else
608         {
609             try{
610             // Two ways to do the same thing. Don't know which way I like best.
611             //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
612             //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
613             //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
614             //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
615             int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
616                           | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
617                           | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
618                           | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
619 
620             
621             destination[ destOffset     ] = (byte)( outBuff >> 16 );
622             destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
623             destination[ destOffset + 2 ] = (byte)( outBuff       );
624 
625             return 3;
626             }catch( Exception e){
627                 System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
628                 System.out.println(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
629                 System.out.println(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
630                 System.out.println(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
631                 return -1;
632             }   //e nd catch
633         }
634     }   // end decodeToBytes
635     
636     
637     
638     
639     /**
640      * Very low-level access to decoding ASCII characters in
641      * the form of a byte array. Does not support automatically
642      * gunzipping or any other "fancy" features.
643      *
644      * @param source The Base64 encoded data
645      * @param off    The offset of where to begin decoding
646      * @param len    The length of characters to decode
647      * @return decoded data
648      * @since 1.3
649      */
650     public static byte[] decode( byte[] source, int off, int len )
651     {
652         int    len34   = len * 3 / 4;
653         byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
654         int    outBuffPosn = 0;
655         
656         byte[] b4        = new byte[4];
657         int    b4Posn    = 0;
658         int    i         = 0;
659         byte   sbiCrop   = 0;
660         byte   sbiDecode = 0;
661         for( i = off; i < off+len; i++ )
662         {
663             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
664             sbiDecode = DECODABET[ sbiCrop ];
665             
666             if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
667             {
668                 if( sbiDecode >= EQUALS_SIGN_ENC )
669                 {
670                     b4[ b4Posn++ ] = sbiCrop;
671                     if( b4Posn > 3 )
672                     {
673                         outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
674                         b4Posn = 0;
675                         
676                         // If that was the equals sign, break out of 'for' loop
677                         if( sbiCrop == EQUALS_SIGN )
678                             break;
679                     }   // end if: quartet built
680                     
681                 }   // end if: equals sign or better
682                 
683             }   // end if: white space, equals sign or better
684             else
685             {
686                 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
687                 return null;
688             }   // end else: 
689         }   // each input character
690                                    
691         byte[] out = new byte[ outBuffPosn ];
692         System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 
693         return out;
694     }   // end decode
695     
696     
697     
698     
699     /**
700      * Decodes data from Base64 notation, automatically
701      * detecting gzip-compressed data and decompressing it.
702      *
703      * @param s the string to decode
704      * @return the decoded data
705      * @since 1.4
706      */
707     public static byte[] decode( String s )
708     {   
709         byte[] bytes = s.getBytes();
710         bytes = decode( bytes, 0, bytes.length );
711   // Degenerate case where the original data
712   // is only a single character, would cause
713   // the array bounds checking below to fail.
714   if (bytes.length < 2) {
715       return bytes;
716   }
717         
718         // Check to see if it's gzip-compressed
719         // GZIP Magic Two-Byte Number: 0x8b1f (35615)
720         int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);       
721         if(
722         bytes != null &&      // In case decoding returned null
723         bytes.length >= 4  && // Don't want to get ArrayIndexOutOfBounds exception
724         java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) 
725         {
726             java.io.ByteArrayInputStream  bais = null;
727             java.util.zip.GZIPInputStream gzis = null;
728             java.io.ByteArrayOutputStream baos = null;
729             byte[] buffer = new byte[2048];
730             int    length = 0;
731             
732             try
733             {
734                 baos = new java.io.ByteArrayOutputStream();
735                 bais = new java.io.ByteArrayInputStream( bytes );
736                 gzis = new java.util.zip.GZIPInputStream( bais );
737                 
738                 while( ( length = gzis.read( buffer ) ) >= 0 )
739                 {
740                     baos.write(buffer,0,length);
741                 }   // end while: reading input
742               
743                 // No error? Get new bytes.
744                 bytes = baos.toByteArray();
745                 
746             }   // end try
747             catch( java.io.IOException e )
748             {
749                 // Just return originally-decoded bytes
750             }   // end catch
751             finally
752             {
753                 try{ baos.close(); } catch( Exception e ){}
754                 try{ gzis.close(); } catch( Exception e ){}
755                 try{ bais.close(); } catch( Exception e ){}
756             }   // end finally
757             
758         }   // end if: gzipped
759         
760         return bytes;
761     }   // end decode
762 
763 
764     
765 
766     /**
767      * Attempts to decode Base64 data and deserialize a Java
768      * Object within. Returns <tt>null</tt> if there was an error.
769      *
770      * @param encodedObject The Base64 data to decode
771      * @return The decoded and deserialized object
772      * @since 1.5
773      */
774     public static Object decodeToObject( String encodedObject )
775     {
776         // Decode and gunzip if necessary
777         byte[] objBytes = decode( encodedObject );
778         
779         java.io.ByteArrayInputStream  bais = null;
780         java.io.ObjectInputStream     ois  = null;
781         Object obj = null;
782         
783         try
784         {
785             bais = new java.io.ByteArrayInputStream( objBytes );
786             ois  = new java.io.ObjectInputStream( bais );
787         
788             obj = ois.readObject();
789         }   // end try
790         catch( java.io.IOException e )
791         {
792             e.printStackTrace();
793             obj = null;
794         }   // end catch
795         catch( java.lang.ClassNotFoundException e )
796         {
797             e.printStackTrace();
798             obj = null;
799         }   // end catch
800         finally
801         {
802             try{ bais.close(); } catch( Exception e ){}
803             try{ ois.close();  } catch( Exception e ){}
804         }   // end finally
805         
806         return obj;
807     }   // end decodeObject
808     
809     
810     /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
811     
812     
813     
814     /**
815      * A {@link Base64#InputStream} will read data from another
816      * {@link java.io.InputStream}, given in the constructor,
817      * and encode/decode to/from Base64 notation on the fly.
818      *
819      * @see Base64
820      * @see java.io.FilterInputStream
821      * @since 1.3
822      */
823     public static class InputStream extends java.io.FilterInputStream
824     {
825         private int     options;        // Options specified
826         private boolean encode;         // Encoding or decoding
827         private int     position;       // Current position in the buffer
828         private byte[]  buffer;         // Small buffer holding converted data
829         private int     bufferLength;   // Length of buffer (3 or 4)
830         private int     numSigBytes;    // Number of meaningful bytes in the buffer
831         private int     lineLength;
832         private boolean breakLines;     // Break lines at less than 80 characters
833         
834         
835         /**
836          * Constructs a {@link Base64#InputStream} in DECODE mode.
837          *
838          * @param in the {@link java.io.InputStream} from which to read data.
839          * @since 1.3
840          */
841         public InputStream( java.io.InputStream in )
842         {   
843             this( in, DECODE );
844         }   // end constructor
845         
846         
847         /**
848          * Constructs a {@link Base64#InputStream} in
849          * either ENCODE or DECODE mode.
850          * <p>
851          * Valid options:<pre>
852          *   ENCODE or DECODE: Encode or Decode as data is read.
853          *   DONT_BREAK_LINES: don't break lines at 76 characters
854          *     (only meaningful when encoding)
855          *     <i>Note: Technically, this makes your encoding non-compliant.</i>
856          * </pre>
857          * <p>
858          * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
859          *
860          *
861          * @param in the {@link java.io.InputStream} from which to read data.
862          * @param options Specified options
863          * @see Base64#ENCODE
864          * @see Base64#DECODE
865          * @see Base64#DONT_BREAK_LINES
866          * @since 2.0
867          */
868         public InputStream( java.io.InputStream in, int options )
869         {   
870             super( in );
871             this.options      = options;
872             this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
873             this.encode       = (options & ENCODE) == ENCODE;
874             this.breakLines = breakLines;
875             this.encode = encode;
876             this.bufferLength = encode ? 4 : 3;
877             this.buffer   = new byte[ bufferLength ];
878             this.position = -1;
879             this.lineLength = 0;
880         }   // end constructor
881         
882         /**
883          * Reads enough of the input stream to convert
884          * to/from Base64 and returns the next byte.
885          *
886          * @return next byte
887          * @since 1.3
888          */
889         public int read() throws java.io.IOException 
890         { 
891             // Do we need to get data?
892             if( position < 0 )
893             {
894                 if( encode )
895                 {
896                     byte[] b3 = new byte[3];
897                     int numBinaryBytes = 0;
898                     for( int i = 0; i < 3; i++ )
899                     {
900                         try
901                         { 
902                             int b = in.read();
903                             
904                             // If end of stream, b is -1.
905                             if( b >= 0 )
906                             {
907                                 b3[i] = (byte)b;
908                                 numBinaryBytes++;
909                             }   // end if: not end of stream
910                             
911                         }   // end try: read
912                         catch( java.io.IOException e )
913                         {   
914                             // Only a problem if we got no data at all.
915                             if( i == 0 )
916                                 throw e;
917                             
918                         }   // end catch
919                     }   // end for: each needed input byte
920                     
921                     if( numBinaryBytes > 0 )
922                     {
923                         encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
924                         position = 0;
925                         numSigBytes = 4;
926                     }   // end if: got data
927                     else
928                     {
929                         return -1;
930                     }   // end else
931                 }   // end if: encoding
932                 
933                 // Else decoding
934                 else
935                 {
936                     byte[] b4 = new byte[4];
937                     int i = 0;
938                     for( i = 0; i < 4; i++ )
939                     {
940                         // Read four "meaningful" bytes:
941                         int b = 0;
942                         do{ b = in.read(); }
943                         while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
944                         
945                         if( b < 0 )
946                             break; // Reads a -1 if end of stream
947                         
948                         b4[i] = (byte)b;
949                     }   // end for: each needed input byte
950                     
951                     if( i == 4 )
952                     {
953                         numSigBytes = decode4to3( b4, 0, buffer, 0 );
954                         position = 0;
955                     }   // end if: got four characters
956                     else if( i == 0 ){
957                         return -1;
958                     }   // end else if: also padded correctly
959                     else
960                     {
961                         // Must have broken out from above.
962                         throw new java.io.IOException( "Improperly padded Base64 input." );
963                     }   // end 
964                     
965                 }   // end else: decode
966             }   // end else: get data
967             
968             // Got data?
969             if( position >= 0 )
970             {
971                 // End of relevant data?
972                 if( /*!encode &&*/ position >= numSigBytes )
973                     return -1;
974                 
975                 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
976                 {
977                     lineLength = 0;
978                     return '\n';
979                 }   // end if
980                 else
981                 {
982                     lineLength++;   // This isn't important when decoding
983                                     // but throwing an extra "if" seems
984                                     // just as wasteful.
985                     
986                     int b = buffer[ position++ ];
987 
988                     if( position >= bufferLength )
989                         position = -1;
990 
991                     return b & 0xFF; // This is how you "cast" a byte that's
992                                      // intended to be unsigned.
993                 }   // end else
994             }   // end if: position >= 0
995             
996             // Else error
997             else
998             {   
999                 // When JDK1.4 is more accepted, use an assertion here.
1000                throw new java.io.IOException( "Error in Base64 code reading stream." );
1001            }   // end else
1002        }   // end read
1003        
1004        
1005        /**
1006         * Calls {@link #read} repeatedly until the end of stream
1007         * is reached or <var>len</var> bytes are read.
1008         * Returns number of bytes read into array or -1 if
1009         * end of stream is encountered.
1010         *
1011         * @param dest array to hold values
1012         * @param off offset for array
1013         * @param len max number of bytes to read into array
1014         * @return bytes read into array or -1 if end of stream is encountered.
1015         * @since 1.3
1016         */
1017        public int read( byte[] dest, int off, int len ) throws java.io.IOException
1018        {
1019            int i;
1020            int b;
1021            for( i = 0; i < len; i++ )
1022            {
1023                b = read();
1024                
1025                //if( b < 0 && i == 0 )
1026                //    return -1;
1027                
1028                if( b >= 0 )
1029                    dest[off + i] = (byte)b;
1030                else if( i == 0 )
1031                    return -1;
1032                else
1033                    break; // Out of 'for' loop
1034            }   // end for: each byte read
1035            return i;
1036        }   // end read
1037        
1038    }   // end inner class InputStream
1039    
1040    
1041    
1042    
1043    
1044    
1045    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
1046    
1047    
1048    
1049    /**
1050     * A {@link Base64#OutputStream} will write data to another
1051     * {@link java.io.OutputStream}, given in the constructor,
1052     * and encode/decode to/from Base64 notation on the fly.
1053     *
1054     * @see Base64
1055     * @see java.io.FilterOutputStream
1056     * @since 1.3
1057     */
1058    public static class OutputStream extends java.io.FilterOutputStream
1059    {
1060        private int     options;
1061        private boolean encode;
1062        private int     position;
1063        private byte[]  buffer;
1064        private int     bufferLength;
1065        private int     lineLength;
1066        private boolean breakLines;
1067        private byte[]  b4; // Scratch used in a few places
1068        private boolean suspendEncoding;
1069        
1070        /**
1071         * Constructs a {@link Base64#OutputStream} in ENCODE mode.
1072         *
1073         * @param out the {@link java.io.OutputStream} to which data will be written.
1074         * @since 1.3
1075         */
1076        public OutputStream( java.io.OutputStream out )
1077        {   
1078            this( out, ENCODE );
1079        }   // end constructor
1080        
1081        
1082        /**
1083         * Constructs a {@link Base64#OutputStream} in
1084         * either ENCODE or DECODE mode.
1085         * <p>
1086         * Valid options:<pre>
1087         *   ENCODE or DECODE: Encode or Decode as data is read.
1088         *   DONT_BREAK_LINES: don't break lines at 76 characters
1089         *     (only meaningful when encoding)
1090         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
1091         * </pre>
1092         * <p>
1093         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1094         *
1095         * @param out the {@link java.io.OutputStream} to which data will be written.
1096         * @param options Specified options.
1097         * @see Base64#ENCODE
1098         * @see Base64#DECODE
1099         * @see Base64#DONT_BREAK_LINES
1100         * @since 1.3
1101         */
1102        public OutputStream( java.io.OutputStream out, int options )
1103        {   
1104            super( out );
1105            this.options      = options;
1106            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1107            this.encode       = (options & ENCODE) == ENCODE;
1108            this.bufferLength = encode ? 3 : 4;
1109            this.buffer       = new byte[ bufferLength ];
1110            this.position     = 0;
1111            this.lineLength   = 0;
1112            this.suspendEncoding = false;
1113            this.b4           = new byte[4];
1114        }   // end constructor
1115        
1116        
1117        /**
1118         * Writes the byte to the output stream after
1119         * converting to/from Base64 notation.
1120         * When encoding, bytes are buffered three
1121         * at a time before the output stream actually
1122         * gets a write() call.
1123         * When decoding, bytes are buffered four
1124         * at a time.
1125         *
1126         * @param theByte the byte to write
1127         * @since 1.3
1128         */
1129        public void write(int theByte) throws java.io.IOException
1130        {
1131            // Encoding suspended?
1132            if( suspendEncoding )
1133            {
1134                super.out.write( theByte );
1135                return;
1136            }   // end if: supsended
1137            
1138            // Encode?
1139            if( encode )
1140            {
1141                buffer[ position++ ] = (byte)theByte;
1142                if( position >= bufferLength )  // Enough to encode.
1143                {
1144                    out.write( encode3to4( b4, buffer, bufferLength ) );
1145
1146                    lineLength += 4;
1147                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
1148                    {
1149                        out.write( NEW_LINE );
1150                        lineLength = 0;
1151                    }   // end if: end of line
1152
1153                    position = 0;
1154                }   // end if: enough to output
1155            }   // end if: encoding
1156
1157            // Else, Decoding
1158            else
1159            {
1160                // Meaningful Base64 character?
1161                if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1162                {
1163                    buffer[ position++ ] = (byte)theByte;
1164                    if( position >= bufferLength )  // Enough to output.
1165                    {
1166                        int len = Base64.decode4to3( buffer, 0, b4, 0 );
1167                        out.write( b4, 0, len );
1168                        //out.write( Base64.decode4to3( buffer ) );
1169                        position = 0;
1170                    }   // end if: enough to output
1171                }   // end if: meaningful base64 character
1172                else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1173                {
1174                    throw new java.io.IOException( "Invalid character in Base64 data." );
1175                }   // end else: not white space either
1176            }   // end else: decoding
1177        }   // end write
1178        
1179        
1180        
1181        /**
1182         * Calls {@link #write} repeatedly until <var>len</var> 
1183         * bytes are written.
1184         *
1185         * @param theBytes array from which to read bytes
1186         * @param off offset for array
1187         * @param len max number of bytes to read into array
1188         * @since 1.3
1189         */
1190        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
1191        {
1192            // Encoding suspended?
1193            if( suspendEncoding )
1194            {
1195                super.out.write( theBytes, off, len );
1196                return;
1197            }   // end if: supsended
1198            
1199            for( int i = 0; i < len; i++ )
1200            {
1201                write( theBytes[ off + i ] );
1202            }   // end for: each byte written
1203            
1204        }   // end write
1205        
1206        
1207        
1208        /**
1209         * Method added by PHIL. [Thanks, PHIL. -Rob]
1210         * This pads the buffer without closing the stream.
1211         */
1212        public void flushBase64() throws java.io.IOException 
1213        {
1214            if( position > 0 )
1215            {
1216                if( encode )
1217                {
1218                    out.write( encode3to4( b4, buffer, position ) );
1219                    position = 0;
1220                }   // end if: encoding
1221                else
1222                {
1223                    throw new java.io.IOException( "Base64 input not properly padded." );
1224                }   // end else: decoding
1225            }   // end if: buffer partially full
1226
1227        }   // end flush
1228
1229        
1230        /** 
1231         * Flushes and closes (I think, in the superclass) the stream. 
1232         *
1233         * @since 1.3
1234         */
1235        public void close() throws java.io.IOException
1236        {
1237            // 1. Ensure that pending characters are written
1238            flushBase64();
1239
1240            // 2. Actually close the stream
1241            // Base class both flushes and closes.
1242            super.close();
1243            
1244            buffer = null;
1245            out    = null;
1246        }   // end close
1247        
1248        
1249        
1250        /**
1251         * Suspends encoding of the stream.
1252         * May be helpful if you need to embed a piece of
1253         * base640-encoded data in a stream.
1254         *
1255         * @since 1.5.1
1256         */
1257        public void suspendEncoding() throws java.io.IOException 
1258        {
1259            flushBase64();
1260            this.suspendEncoding = true;
1261        }   // end suspendEncoding
1262        
1263        
1264        /**
1265         * Resumes encoding of the stream.
1266         * May be helpful if you need to embed a piece of
1267         * base640-encoded data in a stream.
1268         *
1269         * @since 1.5.1
1270         */
1271        public void resumeEncoding()
1272        {
1273            this.suspendEncoding = false;
1274        }   // end resumeEncoding
1275        
1276        
1277        
1278    }   // end inner class OutputStream
1279    
1280    
1281}   // end class Base64