KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mule > util > Base64


1 /*
2  * $Id: Base64.java 4259 2006-12-14 03:12:07Z aperepel $
3  * --------------------------------------------------------------------------------------
4  * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
5  *
6  * The software in this package is published under the terms of the MuleSource MPL
7  * license, a copy of which has been included with this distribution in the
8  * LICENSE.txt file.
9  */

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

60
61 package org.mule.util;
62
63 import java.io.BufferedInputStream JavaDoc;
64 import java.io.ByteArrayInputStream JavaDoc;
65 import java.io.File JavaDoc;
66 import java.io.FileInputStream JavaDoc;
67 import java.io.FileOutputStream JavaDoc;
68 import java.io.FilterInputStream JavaDoc;
69 import java.io.FilterOutputStream JavaDoc;
70 import java.io.IOException JavaDoc;
71 import java.io.ObjectInputStream JavaDoc;
72 import java.io.ObjectOutputStream JavaDoc;
73 import java.io.Serializable JavaDoc;
74 import java.io.UnsupportedEncodingException JavaDoc;
75 import java.util.zip.GZIPInputStream JavaDoc;
76 import java.util.zip.GZIPOutputStream JavaDoc;
77
78 import org.apache.commons.io.IOUtils;
79 import org.apache.commons.io.output.ByteArrayOutputStream;
80
81 public class Base64
82 {
83
84     /* ******** P U B L I C F I E L D S ******** */
85
86     /** No options specified. Value is zero. */
87     public final static int NO_OPTIONS = 0;
88
89     /** Specify encoding. */
90     public final static int ENCODE = 1;
91
92     /** Specify decoding. */
93     public final static int DECODE = 0;
94
95     /** Specify that data should be gzip-compressed. */
96     public final static int GZIP = 2;
97
98     /** Don't break lines when encoding (violates strict Base64 specification) */
99     public final static int DONT_BREAK_LINES = 8;
100
101     /** Preferred encoding. */
102     public final static String JavaDoc PREFERRED_ENCODING = "UTF-8";
103
104     /* ******** P R I V A T E F I E L D S ******** */
105
106     /** Maximum line length (76) of Base64 output. */
107     private final static int MAX_LINE_LENGTH = 76;
108
109     /** The equals sign (=) as a byte. */
110     private final static byte EQUALS_SIGN = (byte)'=';
111
112     /** The new line character (\n) as a byte. */
113     private final static byte NEW_LINE = (byte)'\n';
114
115     /** The 64 valid Base64 values. */
116     private final static byte[] ALPHABET;
117
118     private final static byte[] _NATIVE_ALPHABET =
119     /* May be something funny like EBCDIC */
120     {(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', (byte)'H', (byte)'I',
121         (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q', (byte)'R',
122         (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a',
123         (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j',
124         (byte)'k', (byte)'l', (byte)'m', (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s',
125         (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', (byte)'0', (byte)'1',
126         (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+',
127         (byte)'/'};
128
129     /** Determine which ALPHABET to use. */
130     static
131     {
132         byte[] __bytes;
133         try
134         {
135             __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes(PREFERRED_ENCODING);
136         } // end try
137
catch (java.io.UnsupportedEncodingException JavaDoc use)
138         {
139             __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
140
} // end catch
141
ALPHABET = __bytes;
142     } // end static
143

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

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

185     };
186
187     // I think I end up not using the BAD_ENCODING indicator.
188
// private final static byte BAD_ENCODING = -9; // Indicates error in
189
// encoding
190
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in
191
// encoding
192
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in
193

194     // encoding
195

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

217     private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes)
218     {
219         encode3to4(threeBytes, 0, numSigBytes, b4, 0);
220         return b4;
221     } // end encode3to4
222

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

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

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

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

301     public static String JavaDoc encodeObject(Serializable JavaDoc serializableObject) throws IOException JavaDoc
302     {
303         return encodeObject(serializableObject, NO_OPTIONS);
304     } // end encodeObject
305

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

332     public static String JavaDoc encodeObject(Serializable JavaDoc serializableObject, int options) throws IOException JavaDoc
333     {
334         // Streams
335
ByteArrayOutputStream baos = null;
336         OutputStream JavaDoc b64os = null;
337         ObjectOutputStream JavaDoc oos = null;
338         GZIPOutputStream JavaDoc gzos = null;
339
340         // Isolate options
341
int gzip = (options & GZIP);
342         int dontBreakLines = (options & DONT_BREAK_LINES);
343
344         try
345         {
346             // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
347
baos = new ByteArrayOutputStream(4096);
348             b64os = new Base64.OutputStream(baos, ENCODE | dontBreakLines);
349
350             // GZip?
351
if (gzip == GZIP)
352             {
353                 gzos = new GZIPOutputStream JavaDoc(b64os);
354                 oos = new ObjectOutputStream JavaDoc(gzos);
355             } // end if: gzip
356
else
357             {
358                 oos = new ObjectOutputStream JavaDoc(b64os);
359             }
360
361             oos.writeObject(serializableObject);
362
363             if (gzos != null)
364             {
365                 gzos.finish();
366                 gzos.close();
367             }
368
369             oos.close();
370         } // end try
371
catch (IOException JavaDoc e)
372         {
373             return null;
374         } // end catch
375
finally
376         {
377             IOUtils.closeQuietly(oos);
378             IOUtils.closeQuietly(gzos);
379             IOUtils.closeQuietly(b64os);
380             IOUtils.closeQuietly(baos);
381         } // end finally
382

383         // Return value according to relevant encoding.
384
try
385         {
386             return new String JavaDoc(baos.toByteArray(), PREFERRED_ENCODING);
387         } // end try
388
catch (UnsupportedEncodingException JavaDoc uue)
389         {
390             return new String JavaDoc(baos.toByteArray());
391         } // end catch
392

393     } // end encode
394

395     /**
396      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
397      *
398      * @param source The data to convert
399      * @since 1.4
400      */

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

406     /**
407      * Encodes a byte array into Base64 notation.
408      * <p>
409      * Valid options:
410      *
411      * <pre>
412      * GZIP: gzip-compresses object before encoding it.
413      * DONT_BREAK_LINES: don't break lines at 76 characters
414      * &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
415      * </pre>
416      *
417      * <p>
418      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
419      * <p>
420      * Example:
421      * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
422      *
423      * @param source The data to convert
424      * @param options Specified options
425      * @see Base64#GZIP
426      * @see Base64#DONT_BREAK_LINES
427      * @since 2.0
428      */

429     public static String JavaDoc encodeBytes(byte[] source, int options) throws IOException JavaDoc
430     {
431         return encodeBytes(source, 0, source.length, options);
432     } // end encodeBytes
433

434     /**
435      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
436      *
437      * @param source The data to convert
438      * @param off Offset in array where conversion should begin
439      * @param len Length of data to convert
440      * @since 1.4
441      */

442     public static String JavaDoc encodeBytes(byte[] source, int off, int len) throws IOException JavaDoc
443     {
444         return encodeBytes(source, off, len, NO_OPTIONS);
445     } // end encodeBytes
446

447     /**
448      * Encodes a byte array into Base64 notation.
449      * <p>
450      * Valid options:
451      *
452      * <pre>
453      * GZIP: gzip-compresses object before encoding it.
454      * DONT_BREAK_LINES: don't break lines at 76 characters
455      * &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
456      * </pre>
457      *
458      * <p>
459      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
460      * <p>
461      * Example:
462      * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
463      *
464      * @param source The data to convert
465      * @param off Offset in array where conversion should begin
466      * @param len Length of data to convert
467      * @param options Specified options
468      * @see Base64#GZIP
469      * @see Base64#DONT_BREAK_LINES
470      * @since 2.0
471      */

472     public static String JavaDoc encodeBytes(byte[] source, int off, int len, int options) throws IOException JavaDoc
473     {
474         // Isolate options
475
int dontBreakLines = (options & DONT_BREAK_LINES);
476         int gzip = (options & GZIP);
477
478         // Compress?
479
if (gzip == GZIP)
480         {
481             ByteArrayOutputStream baos = null;
482             GZIPOutputStream JavaDoc gzos = null;
483             Base64.OutputStream b64os = null;
484
485             try
486             {
487                 // GZip -> Base64 -> ByteArray
488
baos = new ByteArrayOutputStream(4096);
489                 b64os = new Base64.OutputStream(baos, ENCODE | dontBreakLines);
490                 gzos = new GZIPOutputStream JavaDoc(b64os);
491
492                 gzos.write(source, off, len);
493                 gzos.finish();
494                 gzos.close();
495             } // end try
496
catch (IOException JavaDoc e)
497             {
498                 throw e;
499             } // end catch
500
finally
501             {
502                 IOUtils.closeQuietly(gzos);
503                 IOUtils.closeQuietly(b64os);
504                 IOUtils.closeQuietly(baos);
505             } // end finally
506

507             // Return value according to relevant encoding.
508
try
509             {
510                 return new String JavaDoc(baos.toByteArray(), PREFERRED_ENCODING);
511             } // end try
512
catch (UnsupportedEncodingException JavaDoc uue)
513             {
514                 return new String JavaDoc(baos.toByteArray());
515             } // end catch
516
} // end if: compress
517

518         // Else, don't compress. Better not to use streams at all then.
519
else
520         {
521             // Convert option to boolean in way that code likes it.
522
boolean breakLines = dontBreakLines == 0;
523
524             int len43 = len * 4 / 3;
525             byte[] outBuff = new byte[(len43) // Main 4:3
526
+ ((len % 3) > 0 ? 4 : 0) // Account for
527
// padding
528
+ (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New
529
// lines
530

531             int d = 0;
532             int e = 0;
533             int len2 = len - 2;
534             int lineLength = 0;
535             for (; d < len2; d += 3, e += 4)
536             {
537                 encode3to4(source, d + off, 3, outBuff, e);
538
539                 lineLength += 4;
540                 if (breakLines && lineLength == MAX_LINE_LENGTH)
541                 {
542                     outBuff[e + 4] = NEW_LINE;
543                     e++;
544                     lineLength = 0;
545                 } // end if: end of line
546
} // en dfor: each piece of array
547

548             if (d < len)
549             {
550                 encode3to4(source, d + off, len - d, outBuff, e);
551                 e += 4;
552             } // end if: some padding needed
553

554             // Return value according to relevant encoding.
555
try
556             {
557                 return new String JavaDoc(outBuff, 0, e, PREFERRED_ENCODING);
558             } // end try
559
catch (UnsupportedEncodingException JavaDoc uue)
560             {
561                 return new String JavaDoc(outBuff, 0, e);
562             } // end catch
563

564         } // end else: don't compress
565

566     } // end encodeBytes
567

568     /* ******** D E C O D I N G M E T H O D S ******** */
569
570     /**
571      * Decodes four bytes from array <var>source</var> and writes the resulting
572      * bytes (up to three of them) to <var>destination</var>. The source and
573      * destination arrays can be manipulated anywhere along their length by
574      * specifying <var>srcOffset</var> and <var>destOffset</var>. This method does
575      * not check to make sure your arrays are large enough to accomodate
576      * <var>srcOffset</var> + 4 for the <var>source</var> array or <var>destOffset</var> +
577      * 3 for the <var>destination</var> array. This method returns the actual number
578      * of bytes that were converted from the Base64 encoding.
579      *
580      * @param source the array to convert
581      * @param srcOffset the index where conversion begins
582      * @param destination the array to hold the conversion
583      * @param destOffset the index where output will be put
584      * @return the number of decoded bytes converted
585      * @since 1.3
586      */

587     private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset)
588     {
589         // Example: Dk==
590
if (source[srcOffset + 2] == EQUALS_SIGN)
591         {
592             // Two ways to do the same thing. Don't know which way I like best.
593
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
594
// )
595
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
596
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
597                           | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
598
599             destination[destOffset] = (byte)(outBuff >>> 16);
600             return 1;
601         }
602
603         // Example: DkL=
604
else if (source[srcOffset + 3] == EQUALS_SIGN)
605         {
606             // Two ways to do the same thing. Don't know which way I like best.
607
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
608
// )
609
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
610
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
611
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
612                           | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
613                           | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
614
615             destination[destOffset] = (byte)(outBuff >>> 16);
616             destination[destOffset + 1] = (byte)(outBuff >>> 8);
617             return 2;
618         }
619
620         // Example: DkLE
621
else
622         {
623             try
624             {
625                 // Two ways to do the same thing. Don't know which way I like
626
// best.
627
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 )
628
// >>> 6 )
629
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
630
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
631
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
632
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
633                               | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
634                               | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
635                               | ((DECODABET[source[srcOffset + 3]] & 0xFF));
636
637                 destination[destOffset] = (byte)(outBuff >> 16);
638                 destination[destOffset + 1] = (byte)(outBuff >> 8);
639                 destination[destOffset + 2] = (byte)(outBuff);
640
641                 return 3;
642             }
643             catch (Exception JavaDoc e)
644             {
645                 // this is not good.
646
StringBuffer JavaDoc msg = new StringBuffer JavaDoc(64);
647                 msg.append(source[srcOffset]).append(": ").append(DECODABET[source[srcOffset]]);
648                 msg.append(source[srcOffset + 1]).append(": ").append(DECODABET[source[srcOffset + 1]]);
649                 msg.append(source[srcOffset + 2]).append(": ").append(DECODABET[source[srcOffset + 2]]);
650                 msg.append(source[srcOffset + 3]).append(": ").append(DECODABET[source[srcOffset + 3]]);
651                 throw new IllegalStateException JavaDoc(msg.toString());
652             } // end catch
653
}
654     } // end decodeToBytes
655

656     /**
657      * Very low-level access to decoding ASCII characters in the form of a byte
658      * array. Does not support automatically gunzipping or any other "fancy"
659      * features.
660      *
661      * @param source The Base64 encoded data
662      * @param off The offset of where to begin decoding
663      * @param len The length of characters to decode
664      * @return decoded data
665      * @since 1.3
666      */

667     public static byte[] decode(byte[] source, int off, int len)
668     {
669         int len34 = len * 3 / 4;
670         byte[] outBuff = new byte[len34]; // Upper limit on size of output
671
int outBuffPosn = 0;
672
673         byte[] b4 = new byte[4];
674         int b4Posn = 0;
675         int i = 0;
676         byte sbiCrop = 0;
677         byte sbiDecode = 0;
678         for (i = off; i < off + len; i++)
679         {
680             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
681
sbiDecode = DECODABET[sbiCrop];
682
683             if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or
684
// better
685
{
686                 if (sbiDecode >= EQUALS_SIGN_ENC)
687                 {
688                     b4[b4Posn++] = sbiCrop;
689                     if (b4Posn > 3)
690                     {
691                         outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
692                         b4Posn = 0;
693
694                         // If that was the equals sign, break out of 'for' loop
695
if (sbiCrop == EQUALS_SIGN) break;
696                     } // end if: quartet built
697

698                 } // end if: equals sign or better
699

700             } // end if: white space, equals sign or better
701
else
702             {
703                 throw new IllegalArgumentException JavaDoc("Bad Base64 input character at " + i + ": " + source[i]
704                                 + "(decimal)");
705             } // end else:
706
} // each input character
707

708         byte[] out = new byte[outBuffPosn];
709         System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
710         return out;
711     } // end decode
712

713     /**
714      * Decodes data from Base64 notation, automatically detecting gzip-compressed
715      * data and decompressing it.
716      *
717      * @param s the string to decode
718      * @return the decoded data
719      * @since 1.4
720      */

721     public static byte[] decode(String JavaDoc s)
722     {
723         byte[] bytes;
724         try
725         {
726             bytes = s.getBytes(PREFERRED_ENCODING);
727         } // end try
728
catch (UnsupportedEncodingException JavaDoc uee)
729         {
730             bytes = s.getBytes();
731         } // end catch
732
// </change>
733

734         // Decode
735
bytes = decode(bytes, 0, bytes.length);
736
737         // Check to see if it's gzip-compressed
738
// GZIP Magic Two-Byte Number: 0x8b1f (35615)
739
if (bytes != null && bytes.length >= 4)
740         {
741
742             int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
743             if (GZIPInputStream.GZIP_MAGIC == head)
744             {
745                 ByteArrayInputStream JavaDoc bais = null;
746                 GZIPInputStream JavaDoc gzis = null;
747                 ByteArrayOutputStream baos = null;
748                 byte[] buffer = new byte[4096];
749                 int length = 0;
750
751                 try
752                 {
753                     baos = new ByteArrayOutputStream(4096);
754                     bais = new ByteArrayInputStream JavaDoc(bytes);
755                     gzis = new GZIPInputStream JavaDoc(bais);
756
757                     while ((length = gzis.read(buffer)) >= 0)
758                     {
759                         baos.write(buffer, 0, length);
760                     } // end while: reading input
761

762                     // No error? Get new bytes.
763
bytes = baos.toByteArray();
764
765                 } // end try
766
catch (IOException JavaDoc e)
767                 {
768                     // Just return originally-decoded bytes
769
} // end catch
770
finally
771                 {
772                     IOUtils.closeQuietly(baos);
773                     IOUtils.closeQuietly(gzis);
774                     IOUtils.closeQuietly(bais);
775                 } // end finally
776

777             } // end if: gzipped
778
} // end if: bytes.length >= 2
779

780         return bytes;
781     } // end decode
782

783     /**
784      * Attempts to decode Base64 data and deserialize a Java Object within. Returns
785      * <tt>null</tt> if there was an error.
786      *
787      * @param encodedObject The Base64 data to decode
788      * @return The decoded and deserialized object
789      * @since 1.5
790      */

791     public static Object JavaDoc decodeToObject(String JavaDoc encodedObject) throws IOException JavaDoc, ClassNotFoundException JavaDoc
792     {
793         // Decode and gunzip if necessary
794
byte[] objBytes = decode(encodedObject);
795
796         ByteArrayInputStream JavaDoc bais = null;
797         ObjectInputStream JavaDoc ois = null;
798         Object JavaDoc obj = null;
799
800         try
801         {
802             bais = new ByteArrayInputStream JavaDoc(objBytes);
803             ois = new ObjectInputStream JavaDoc(bais);
804             obj = ois.readObject();
805         } // end try
806
catch (IOException JavaDoc e)
807         {
808             throw e;
809         } // end catch
810
catch (java.lang.ClassNotFoundException JavaDoc e)
811         {
812             throw e;
813         } // end catch
814
finally
815         {
816             IOUtils.closeQuietly(bais);
817             IOUtils.closeQuietly(ois);
818         } // end finally
819

820         return obj;
821     } // end decodeObject
822

823     /**
824      * Convenience method for encoding data to a file.
825      *
826      * @param dataToEncode byte array of data to encode in base64 form
827      * @param filename Filename for saving encoded data
828      * @since 2.1
829      */

830     public static void encodeToFile(byte[] dataToEncode, String JavaDoc filename) throws IOException JavaDoc
831     {
832         Base64.OutputStream bos = null;
833
834         try
835         {
836             bos = new Base64.OutputStream(new FileOutputStream JavaDoc(filename), Base64.ENCODE);
837             bos.write(dataToEncode);
838         } // end try
839
catch (IOException JavaDoc e)
840         {
841             throw e;
842         } // end catch: IOException
843
finally
844         {
845             IOUtils.closeQuietly(bos);
846         } // end finally
847
} // end encodeToFile
848

849     /**
850      * Convenience method for decoding data to a file.
851      *
852      * @param dataToDecode Base64-encoded data as a string
853      * @param filename Filename for saving decoded data
854      * @since 2.1
855      */

856     public static void decodeToFile(String JavaDoc dataToDecode, String JavaDoc filename) throws IOException JavaDoc
857     {
858         Base64.OutputStream bos = null;
859
860         try
861         {
862             bos = new Base64.OutputStream(new FileOutputStream JavaDoc(filename), Base64.DECODE);
863             bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
864         } // end try
865
catch (IOException JavaDoc e)
866         {
867             throw e;
868         } // end catch: IOException
869
finally
870         {
871             IOUtils.closeQuietly(bos);
872         } // end finally
873
} // end decodeToFile
874

875     /**
876      * Convenience method for reading a base64-encoded file and decoding it.
877      *
878      * @param filename Filename for reading encoded data
879      * @return decoded byte array or null if unsuccessful
880      * @since 2.1
881      */

882     public static byte[] decodeFromFile(String JavaDoc filename) throws IOException JavaDoc
883     {
884         byte[] decodedData = null;
885         Base64.InputStream bis = null;
886
887         try
888         {
889             // Set up some useful variables
890
File JavaDoc file = FileUtils.newFile(filename);
891             byte[] buffer = null;
892             int length = 0;
893             int numBytes = 0;
894
895             // Check for size of file
896
if (file.length() > Integer.MAX_VALUE)
897             {
898                 throw new IllegalArgumentException JavaDoc("File is too big for this convenience method ("
899                                                    + file.length() + " bytes).");
900             } // end if: file too big for int index
901
buffer = new byte[(int)file.length()];
902
903             // Open a stream
904
bis = new Base64.InputStream(new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(file)), Base64.DECODE);
905
906             // Read until done
907
while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
908             {
909                 length += numBytes;
910             }
911
912             // Save in a variable to return
913
decodedData = new byte[length];
914             System.arraycopy(buffer, 0, decodedData, 0, length);
915         } // end try
916
catch (IOException JavaDoc e)
917         {
918             throw e;
919         } // end catch: IOException
920
finally
921         {
922             IOUtils.closeQuietly(bis);
923         } // end finally
924

925         return decodedData;
926     } // end decodeFromFile
927

928     /**
929      * Convenience method for reading a binary file and base64-encoding it.
930      *
931      * @param filename Filename for reading binary data
932      * @return base64-encoded string or null if unsuccessful
933      * @since 2.1
934      */

935     public static String JavaDoc encodeFromFile(String JavaDoc filename) throws IOException JavaDoc
936     {
937         String JavaDoc encodedData = null;
938         Base64.InputStream bis = null;
939
940         try
941         {
942             // Set up some useful variables
943
File JavaDoc file = FileUtils.newFile(filename);
944             byte[] buffer = new byte[(int)(file.length() * 1.4)];
945             int length = 0;
946             int numBytes = 0;
947
948             // Open a stream
949
bis = new Base64.InputStream(new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(file)), Base64.ENCODE);
950
951             // Read until done
952
while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
953             {
954                 length += numBytes;
955             }
956
957             // Save in a variable to return
958
encodedData = new String JavaDoc(buffer, 0, length, Base64.PREFERRED_ENCODING);
959         } // end try
960
catch (IOException JavaDoc e)
961         {
962             throw e;
963         } // end catch: IOException
964
finally
965         {
966             IOUtils.closeQuietly(bis);
967         } // end finally
968

969         return encodedData;
970     } // end encodeFromFile
971

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

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

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

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

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

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

1036        /**
1037         * Reads enough of the input stream to convert to/from Base64 and returns the
1038         * next byte.
1039         *
1040         * @return next byte
1041         * @since 1.3
1042         */

1043        public int read() throws IOException JavaDoc
1044        {
1045            // Do we need to get data?
1046
if (position < 0)
1047            {
1048                if (encode)
1049                {
1050                    byte[] b3 = new byte[3];
1051                    int numBinaryBytes = 0;
1052                    for (int i = 0; i < 3; i++)
1053                    {
1054                        try
1055                        {
1056                            int b = in.read();
1057
1058                            // If end of stream, b is -1.
1059
if (b >= 0)
1060                            {
1061                                b3[i] = (byte)b;
1062                                numBinaryBytes++;
1063                            } // end if: not end of stream
1064

1065                        } // end try: read
1066
catch (IOException JavaDoc e)
1067                        {
1068                            // Only a problem if we got no data at all.
1069
if (i == 0) throw e;
1070
1071                        } // end catch
1072
} // end for: each needed input byte
1073

1074                    if (numBinaryBytes > 0)
1075                    {
1076                        encode3to4(b3, 0, numBinaryBytes, buffer, 0);
1077                        position = 0;
1078                        numSigBytes = 4;
1079                    } // end if: got data
1080
else
1081                    {
1082                        return -1;
1083                    } // end else
1084
} // end if: encoding
1085

1086                // Else decoding
1087
else
1088                {
1089                    byte[] b4 = new byte[4];
1090                    int i = 0;
1091                    for (i = 0; i < 4; i++)
1092                    {
1093                        // Read four "meaningful" bytes:
1094
int b = 0;
1095                        do
1096                        {
1097                            b = in.read();
1098                        }
1099                        while (b >= 0 && DECODABET[b & 0x7f] <= WHITE_SPACE_ENC);
1100
1101                        if (b < 0) break; // Reads a -1 if end of stream
1102

1103                        b4[i] = (byte)b;
1104                    } // end for: each needed input byte
1105

1106                    if (i == 4)
1107                    {
1108                        numSigBytes = decode4to3(b4, 0, buffer, 0);
1109                        position = 0;
1110                    } // end if: got four characters
1111
else if (i == 0)
1112                    {
1113                        return -1;
1114                    } // end else if: also padded correctly
1115
else
1116                    {
1117                        // Must have broken out from above.
1118
throw new IOException JavaDoc("Improperly padded Base64 input.");
1119                    } // end
1120

1121                } // end else: decode
1122
} // end else: get data
1123

1124            // Got data?
1125
if (position >= 0)
1126            {
1127                // End of relevant data?
1128
if ( /* !encode && */position >= numSigBytes) return -1;
1129
1130                if (encode && breakLines && lineLength >= MAX_LINE_LENGTH)
1131                {
1132                    lineLength = 0;
1133                    return '\n';
1134                } // end if
1135
else
1136                {
1137                    lineLength++; // This isn't important when decoding
1138
// but throwing an extra "if" seems
1139
// just as wasteful.
1140

1141                    int b = buffer[position++];
1142
1143                    if (position >= bufferLength) position = -1;
1144
1145                    return b & 0xFF; // This is how you "cast" a byte that's
1146
// intended to be unsigned.
1147
} // end else
1148
} // end if: position >= 0
1149

1150            // Else error
1151
else
1152            {
1153                // When JDK1.4 is more accepted, use an assertion here.
1154
throw new IOException JavaDoc("Error in Base64 code reading stream.");
1155            } // end else
1156
} // end read
1157

1158        /**
1159         * Calls {@link #read()} repeatedly until the end of stream is reached or
1160         * <var>len</var> bytes are read. Returns number of bytes read into array or
1161         * -1 if end of stream is encountered.
1162         *
1163         * @param dest array to hold values
1164         * @param off offset for array
1165         * @param len max number of bytes to read into array
1166         * @return bytes read into array or -1 if end of stream is encountered.
1167         * @since 1.3
1168         */

1169        public int read(byte[] dest, int off, int len) throws IOException JavaDoc
1170        {
1171            int i;
1172            int b;
1173            for (i = 0; i < len; i++)
1174            {
1175                b = read();
1176
1177                // if( b < 0 && i == 0 )
1178
// return -1;
1179

1180                if (b >= 0)
1181                    dest[off + i] = (byte)b;
1182                else if (i == 0)
1183                    return -1;
1184                else
1185                    break; // Out of 'for' loop
1186
} // end for: each byte read
1187
return i;
1188        } // end read
1189

1190    } // end inner class InputStream
1191

1192    /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1193
1194    /**
1195     * A {@link Base64.OutputStream} will write data to another <tt>OutputStream</tt>,
1196     * given in the constructor, and encode/decode to/from Base64 notation on the
1197     * fly.
1198     *
1199     * @see Base64
1200     * @since 1.3
1201     */

1202    public static class OutputStream extends FilterOutputStream JavaDoc
1203    {
1204        private boolean encode;
1205        private int position;
1206        private byte[] buffer;
1207        private int bufferLength;
1208        private int lineLength;
1209        private boolean breakLines;
1210        private byte[] b4; // Scratch used in a few places
1211
private boolean suspendEncoding;
1212
1213        /**
1214         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1215         *
1216         * @param out the <tt>OutputStream</tt> to which data will be written.
1217         * @since 1.3
1218         */

1219        public OutputStream(java.io.OutputStream JavaDoc out)
1220        {
1221            this(out, ENCODE);
1222        } // end constructor
1223

1224        /**
1225         * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE mode.
1226         * <p>
1227         * Valid options:
1228         *
1229         * <pre>
1230         * ENCODE or DECODE: Encode or Decode as data is read.
1231         * DONT_BREAK_LINES: don't break lines at 76 characters
1232         * (only meaningful when encoding)
1233         * &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
1234         * </pre>
1235         *
1236         * <p>
1237         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1238         *
1239         * @param out the <tt>OutputStream</tt> to which data will be written.
1240         * @param options Specified options.
1241         * @see Base64#ENCODE
1242         * @see Base64#DECODE
1243         * @see Base64#DONT_BREAK_LINES
1244         * @since 1.3
1245         */

1246        public OutputStream(java.io.OutputStream JavaDoc out, int options)
1247        {
1248            super(out);
1249            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1250            this.encode = (options & ENCODE) == ENCODE;
1251            this.bufferLength = encode ? 3 : 4;
1252            this.buffer = new byte[bufferLength];
1253            this.position = 0;
1254            this.lineLength = 0;
1255            this.suspendEncoding = false;
1256            this.b4 = new byte[4];
1257        } // end constructor
1258

1259        /**
1260         * Writes the byte to the output stream after converting to/from Base64
1261         * notation. When encoding, bytes are buffered three at a time before the
1262         * output stream actually gets a write() call. When decoding, bytes are
1263         * buffered four at a time.
1264         *
1265         * @param theByte the byte to write
1266         * @since 1.3
1267         */

1268        public void write(int theByte) throws IOException JavaDoc
1269        {
1270            // Encoding suspended?
1271
if (suspendEncoding)
1272            {
1273                super.out.write(theByte);
1274                return;
1275            } // end if: supsended
1276

1277            // Encode?
1278
if (encode)
1279            {
1280                buffer[position++] = (byte)theByte;
1281                if (position >= bufferLength) // Enough to encode.
1282
{
1283                    out.write(encode3to4(b4, buffer, bufferLength));
1284
1285                    lineLength += 4;
1286                    if (breakLines && lineLength >= MAX_LINE_LENGTH)
1287                    {
1288                        out.write(NEW_LINE);
1289                        lineLength = 0;
1290                    } // end if: end of line
1291

1292                    position = 0;
1293                } // end if: enough to output
1294
} // end if: encoding
1295

1296            // Else, Decoding
1297
else
1298            {
1299                // Meaningful Base64 character?
1300
if (DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC)
1301                {
1302                    buffer[position++] = (byte)theByte;
1303                    if (position >= bufferLength) // Enough to output.
1304
{
1305                        int len = Base64.decode4to3(buffer, 0, b4, 0);
1306                        out.write(b4, 0, len);
1307                        // out.write( Base64.decode4to3( buffer ) );
1308
position = 0;
1309                    } // end if: enough to output
1310
} // end if: meaningful base64 character
1311
else if (DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC)
1312                {
1313                    throw new IOException JavaDoc("Invalid character in Base64 data.");
1314                } // end else: not white space either
1315
} // end else: decoding
1316
} // end write
1317

1318        /**
1319         * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are
1320         * written.
1321         *
1322         * @param theBytes array from which to read bytes
1323         * @param off offset for array
1324         * @param len max number of bytes to read into array
1325         * @since 1.3
1326         */

1327        public void write(byte[] theBytes, int off, int len) throws IOException JavaDoc
1328        {
1329            // Encoding suspended?
1330
if (suspendEncoding)
1331            {
1332                super.out.write(theBytes, off, len);
1333                return;
1334            } // end if: supsended
1335

1336            for (int i = 0; i < len; i++)
1337            {
1338                write(theBytes[off + i]);
1339            } // end for: each byte written
1340

1341        } // end write
1342

1343        /**
1344         * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer without
1345         * closing the stream.
1346         */

1347        public void flushBase64() throws IOException JavaDoc
1348        {
1349            if (position > 0)
1350            {
1351                if (encode)
1352                {
1353                    out.write(encode3to4(b4, buffer, position));
1354                    position = 0;
1355                } // end if: encoding
1356
else
1357                {
1358                    throw new IOException JavaDoc("Base64 input not properly padded.");
1359                } // end else: decoding
1360
} // end if: buffer partially full
1361

1362        } // end flush
1363

1364        /**
1365         * Flushes and closes (I think, in the superclass) the stream.
1366         *
1367         * @since 1.3
1368         */

1369        public void close() throws IOException JavaDoc
1370        {
1371            // 1. Ensure that pending characters are written
1372
flushBase64();
1373
1374            // 2. Actually close the stream
1375
// Base class both flushes and closes.
1376
super.close();
1377
1378            buffer = null;
1379            out = null;
1380        } // end close
1381

1382        /**
1383         * Suspends encoding of the stream. May be helpful if you need to embed a
1384         * piece of base640-encoded data in a stream.
1385         *
1386         * @since 1.5.1
1387         */

1388        public void suspendEncoding() throws IOException JavaDoc
1389        {
1390            flushBase64();
1391            this.suspendEncoding = true;
1392        } // end suspendEncoding
1393

1394        /**
1395         * Resumes encoding of the stream. May be helpful if you need to embed a
1396         * piece of base640-encoded data in a stream.
1397         *
1398         * @since 1.5.1
1399         */

1400        public void resumeEncoding()
1401        {
1402            this.suspendEncoding = false;
1403        } // end resumeEncoding
1404

1405    } // end inner class OutputStream
1406

1407} // end class Base64
1408
Popular Tags