1 package net.sourceforge.phpeclipse.xdebug.core;
4 * Encodes and decodes to and from Base64 notation.
10 * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods.
11 * Added some convenience methods for reading and writing to and from files.</li>
12 * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on
13 * systems with other encodings (like EBCDIC).</li>
14 * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
15 * encoded data was a single byte.</li>
16 * <li>v2.0 - I got rid of methods that used booleans to set options. Now
17 * everything is more consolidated and cleaner. The code now detects when data
18 * that's being decoded is gzip-compressed and will decompress it automatically.
19 * Generally things are cleaner. You'll probably have to change some method
20 * calls that you were making to support the new options format (<tt>int</tt>s
21 * that you "OR" together).</li>
22 * <li>v1.5.1 - Fixed bug when decompressing and decoding to a byte[] using
23 * <tt>decode( String s, boolean gzipCompressed )</tt>. Added the ability to
24 * "suspend" encoding in the Output Stream so you can turn on and off the
25 * encoding if you need to embed base64 data in an otherwise "normal" stream
26 * (like an XML file).</li>
27 * <li>v1.5 - Output stream pases on flush() command but doesn't do anything
28 * itself. This helps when using GZIP streams. Added the ability to
29 * GZip-compress objects before encoding them.</li>
30 * <li>v1.4 - Added helper methods to read/write files.</li>
31 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
32 * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input
33 * stream where last buffer being read, if not completely full, was not
35 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the
37 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
41 * I am placing this code in the Public Domain. Do with it as you will. This
42 * software comes with no guarantees or warranties but with plenty of
43 * well-wishing instead! Please visit <a
44 * href="http://iharder.net/base64">http://iharder.net/base64</a> periodically
45 * to check for updates or to contribute improvements.
48 * @author Robert Harder
49 * @author rob@iharder.net
54 /* ******** P U B L I C F I E L D S ******** */
56 /** No options specified. Value is zero. */
57 public final static int NO_OPTIONS = 0;
59 /** Specify encoding. */
60 public final static int ENCODE = 1;
62 /** Specify decoding. */
63 public final static int DECODE = 0;
65 /** Specify that data should be gzip-compressed. */
66 public final static int GZIP = 2;
68 /** Don't break lines when encoding (violates strict Base64 specification) */
69 public final static int DONT_BREAK_LINES = 8;
71 /* ******** P R I V A T E F I E L D S ******** */
73 /** Maximum line length (76) of Base64 output. */
74 private final static int MAX_LINE_LENGTH = 76;
76 /** The equals sign (=) as a byte. */
77 private final static byte EQUALS_SIGN = (byte) '=';
79 /** The new line character (\n) as a byte. */
80 private final static byte NEW_LINE = (byte) '\n';
82 /** Preferred encoding. */
83 private final static String PREFERRED_ENCODING = "UTF-8";
85 /** The 64 valid Base64 values. */
86 private final static byte[] ALPHABET;
88 private final static byte[] _NATIVE_ALPHABET = /*
89 * May be something funny
92 { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
93 (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
94 (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
95 (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
96 (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
97 (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
98 (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
99 (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
100 (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
101 (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
102 (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
103 (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
104 (byte) '9', (byte) '+', (byte) '/' };
106 /** Determine which ALPHABET to use. */
110 __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
111 .getBytes(PREFERRED_ENCODING);
113 catch (java.io.UnsupportedEncodingException use) {
114 __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
120 * Translates a Base64 value to either its 6-bit reconstruction value or a
121 * negative number indicating some other meaning.
123 private final static byte[] DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9,
125 -5, -5, // Whitespace: Tab and Linefeed
126 -9, -9, // Decimal 11 - 12
127 -5, // Whitespace: Carriage Return
128 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
130 -9, -9, -9, -9, -9, // Decimal 27 - 31
131 -5, // Whitespace: Space
132 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
133 62, // Plus sign at decimal 43
134 -9, -9, -9, // Decimal 44 - 46
135 63, // Slash at decimal 47
136 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
137 -9, -9, -9, // Decimal 58 - 60
138 -1, // Equals sign at decimal 61
139 -9, -9, -9, // Decimal 62 - 64
140 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A'
142 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
144 -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
145 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
147 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
149 -9, -9, -9, -9 // Decimal 123 - 126
151 * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
152 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
153 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
154 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
155 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
156 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
157 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
158 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
159 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
160 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255
164 // I think I end up not using the BAD_ENCODING indicator.
165 // private final static byte BAD_ENCODING = -9; // Indicates error in
167 private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in
170 private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in
173 /** Defeats instantiation. */
177 /* ******** E N C O D I N G M E T H O D S ******** */
180 * Encodes up to the first three bytes of array <var>threeBytes</var> and
181 * returns a four-byte array in Base64 notation. The actual number of
182 * significant bytes in your array is given by <var>numSigBytes</var>. The
183 * array <var>threeBytes</var> needs only be as big as <var>numSigBytes</var>.
184 * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
187 * A reusable byte array to reduce array instantiation
189 * the array to convert
191 * the number of significant bytes in your array
192 * @return four byte array in Base64 notation.
195 private static byte[] encode3to4(byte[] b4, byte[] threeBytes,
197 encode3to4(threeBytes, 0, numSigBytes, b4, 0);
202 * Encodes up to three bytes of the array <var>source</var> and writes the
203 * resulting four Base64 bytes to <var>destination</var>. The source and
204 * destination arrays can be manipulated anywhere along their length by
205 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
206 * does not check to make sure your arrays are large enough to accomodate
207 * <var>srcOffset</var> + 3 for the <var>source</var> array or
208 * <var>destOffset</var> + 4 for the <var>destination</var> array. The
209 * actual number of significant bytes in your array is given by
210 * <var>numSigBytes</var>.
213 * the array to convert
215 * the index where conversion begins
217 * the number of significant bytes in your array
219 * the array to hold the conversion
221 * the index where output will be put
222 * @return the <var>destination</var> array
225 private static byte[] encode3to4(byte[] source, int srcOffset,
226 int numSigBytes, byte[] destination, int destOffset) {
228 // 01234567890123456789012345678901 Bit position
229 // --------000000001111111122222222 Array position from threeBytes
230 // --------| || || || | Six bit groups to index ALPHABET
231 // >>18 >>12 >> 6 >> 0 Right shift necessary
232 // 0x3f 0x3f 0x3f Additional AND
234 // Create buffer with zero-padding if there are only one or two
235 // significant bytes passed in the array.
236 // We have to shift left 24 in order to flush out the 1's that appear
237 // when Java treats a value as negative that is cast from a byte to an
239 int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
240 | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
241 | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
243 switch (numSigBytes) {
245 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
246 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
247 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
248 destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
252 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
253 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
254 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
255 destination[destOffset + 3] = EQUALS_SIGN;
259 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
260 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
261 destination[destOffset + 2] = EQUALS_SIGN;
262 destination[destOffset + 3] = EQUALS_SIGN;
271 * Serializes an object and returns the Base64-encoded version of that
272 * serialized object. If the object cannot be serialized or there is another
273 * error, the method will return <tt>null</tt>. The object is not
274 * GZip-compressed before being encoded.
276 * @param serializableObject
277 * The object to encode
278 * @return The Base64-encoded object
281 public static String encodeObject(java.io.Serializable serializableObject) {
282 return encodeObject(serializableObject, NO_OPTIONS);
283 } // end encodeObject
286 * Serializes an object and returns the Base64-encoded version of that
287 * serialized object. If the object cannot be serialized or there is another
288 * error, the method will return <tt>null</tt>.
293 * GZIP: gzip-compresses object before encoding it.
294 * DONT_BREAK_LINES: don't break lines at 76 characters
295 * <i>Note: Technically, this makes your encoding non-compliant.</i>
299 * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
302 * <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
304 * @param serializableObject
305 * The object to encode
308 * @return The Base64-encoded object
310 * @see Base64#DONT_BREAK_LINES
313 public static String encodeObject(java.io.Serializable serializableObject,
316 java.io.ByteArrayOutputStream baos = null;
317 java.io.OutputStream b64os = null;
318 java.io.ObjectOutputStream oos = null;
319 java.util.zip.GZIPOutputStream gzos = null;
322 int gzip = (options & GZIP);
323 int dontBreakLines = (options & DONT_BREAK_LINES);
326 // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
327 baos = new java.io.ByteArrayOutputStream();
328 b64os = new Base64.OutputStream(baos, ENCODE | dontBreakLines);
332 gzos = new java.util.zip.GZIPOutputStream(b64os);
333 oos = new java.io.ObjectOutputStream(gzos);
336 oos = new java.io.ObjectOutputStream(b64os);
338 oos.writeObject(serializableObject);
340 catch (java.io.IOException e) {
347 } catch (Exception e) {
351 } catch (Exception e) {
355 } catch (Exception e) {
359 } catch (Exception e) {
363 // Return value according to relevant encoding.
365 return new String(baos.toByteArray(), PREFERRED_ENCODING);
367 catch (java.io.UnsupportedEncodingException uue) {
368 return new String(baos.toByteArray());
374 * Encodes a byte array into Base64 notation. Does not GZip-compress data.
377 * The data to convert
380 public static String encodeBytes(byte[] source) {
381 return encodeBytes(source, 0, source.length, NO_OPTIONS);
385 * Encodes a byte array into Base64 notation.
390 * GZIP: gzip-compresses object before encoding it.
391 * DONT_BREAK_LINES: don't break lines at 76 characters
392 * <i>Note: Technically, this makes your encoding non-compliant.</i>
396 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
399 * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
403 * The data to convert
407 * @see Base64#DONT_BREAK_LINES
410 public static String encodeBytes(byte[] source, int options) {
411 return encodeBytes(source, 0, source.length, options);
415 * Encodes a byte array into Base64 notation. Does not GZip-compress data.
418 * The data to convert
420 * Offset in array where conversion should begin
422 * Length of data to convert
425 public static String encodeBytes(byte[] source, int off, int len) {
426 return encodeBytes(source, off, len, NO_OPTIONS);
430 * Encodes a byte array into Base64 notation.
435 * GZIP: gzip-compresses object before encoding it.
436 * DONT_BREAK_LINES: don't break lines at 76 characters
437 * <i>Note: Technically, this makes your encoding non-compliant.</i>
441 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
444 * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
448 * The data to convert
450 * Offset in array where conversion should begin
452 * Length of data to convert
456 * @see Base64#DONT_BREAK_LINES
459 public static String encodeBytes(byte[] source, int off, int len,
462 int dontBreakLines = (options & DONT_BREAK_LINES);
463 int gzip = (options & GZIP);
467 java.io.ByteArrayOutputStream baos = null;
468 java.util.zip.GZIPOutputStream gzos = null;
469 Base64.OutputStream b64os = null;
472 // GZip -> Base64 -> ByteArray
473 baos = new java.io.ByteArrayOutputStream();
474 b64os = new Base64.OutputStream(baos, ENCODE | dontBreakLines);
475 gzos = new java.util.zip.GZIPOutputStream(b64os);
477 gzos.write(source, off, len);
480 catch (java.io.IOException e) {
487 } catch (Exception e) {
491 } catch (Exception e) {
495 } catch (Exception e) {
499 // Return value according to relevant encoding.
501 return new String(baos.toByteArray(), PREFERRED_ENCODING);
503 catch (java.io.UnsupportedEncodingException uue) {
504 return new String(baos.toByteArray());
506 } // end if: compress
508 // Else, don't compress. Better not to use streams at all then.
510 // Convert option to boolean in way that code likes it.
511 boolean breakLines = dontBreakLines == 0;
513 int len43 = len * 4 / 3;
514 byte[] outBuff = new byte[(len43) // Main 4:3
515 + ((len % 3) > 0 ? 4 : 0) // Account for padding
516 + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New
522 for (; d < len2; d += 3, e += 4) {
523 encode3to4(source, d + off, 3, outBuff, e);
526 if (breakLines && lineLength == MAX_LINE_LENGTH) {
527 outBuff[e + 4] = NEW_LINE;
530 } // end if: end of line
531 } // en dfor: each piece of array
534 encode3to4(source, d + off, len - d, outBuff, e);
536 } // end if: some padding needed
538 // Return value according to relevant encoding.
540 return new String(outBuff, 0, e, PREFERRED_ENCODING);
542 catch (java.io.UnsupportedEncodingException uue) {
543 return new String(outBuff, 0, e);
546 } // end else: don't compress
550 /* ******** D E C O D I N G M E T H O D S ******** */
553 * Decodes four bytes from array <var>source</var> and writes the resulting
554 * bytes (up to three of them) to <var>destination</var>. The source and
555 * destination arrays can be manipulated anywhere along their length by
556 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
557 * does not check to make sure your arrays are large enough to accomodate
558 * <var>srcOffset</var> + 4 for the <var>source</var> array or
559 * <var>destOffset</var> + 3 for the <var>destination</var> array. This
560 * method returns the actual number of bytes that were converted from the
565 * the array to convert
567 * the index where conversion begins
569 * the array to hold the conversion
571 * the index where output will be put
572 * @return the number of decoded bytes converted
575 private static int decode4to3(byte[] source, int srcOffset,
576 byte[] destination, int destOffset) {
578 if (source[srcOffset + 2] == EQUALS_SIGN) {
579 // Two ways to do the same thing. Don't know which way I like best.
580 // 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);
586 destination[destOffset] = (byte) (outBuff >>> 16);
591 else if (source[srcOffset + 3] == EQUALS_SIGN) {
592 // Two ways to do the same thing. Don't know which way I like best.
593 // 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);
601 destination[destOffset] = (byte) (outBuff >>> 16);
602 destination[destOffset + 1] = (byte) (outBuff >>> 8);
609 // Two ways to do the same thing. Don't know which way I like
611 // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 )
613 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
614 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
615 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
616 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
617 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
618 | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
619 | ((DECODABET[source[srcOffset + 3]] & 0xFF));
621 destination[destOffset] = (byte) (outBuff >> 16);
622 destination[destOffset + 1] = (byte) (outBuff >> 8);
623 destination[destOffset + 2] = (byte) (outBuff);
626 } catch (Exception e) {
627 System.out.println("" + source[srcOffset] + ": "
628 + (DECODABET[source[srcOffset]]));
629 System.out.println("" + source[srcOffset + 1] + ": "
630 + (DECODABET[source[srcOffset + 1]]));
631 System.out.println("" + source[srcOffset + 2] + ": "
632 + (DECODABET[source[srcOffset + 2]]));
633 System.out.println("" + source[srcOffset + 3] + ": "
634 + (DECODABET[source[srcOffset + 3]]));
638 } // end decodeToBytes
641 * Very low-level access to decoding ASCII characters in the form of a byte
642 * array. Does not support automatically gunzipping or any other "fancy"
646 * The Base64 encoded data
648 * The offset of where to begin decoding
650 * The length of characters to decode
651 * @return decoded data
654 public static byte[] decode(byte[] source, int off, int len) {
655 int len34 = len * 3 / 4;
656 byte[] outBuff = new byte[len34]; // Upper limit on size of output
659 byte[] b4 = new byte[4];
664 for (i = off; i < off + len; i++) {
665 sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
666 sbiDecode = DECODABET[sbiCrop];
668 if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or
671 if (sbiDecode >= EQUALS_SIGN_ENC) {
672 b4[b4Posn++] = sbiCrop;
674 outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
677 // If that was the equals sign, break out of 'for' loop
678 if (sbiCrop == EQUALS_SIGN)
680 } // end if: quartet built
682 } // end if: equals sign or better
684 } // end if: white space, equals sign or better
686 System.err.println("Bad Base64 input character at " + i + ": "
687 + source[i] + "(decimal)");
690 } // each input character
692 byte[] out = new byte[outBuffPosn];
693 System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
698 * Decodes data from Base64 notation, automatically detecting
699 * gzip-compressed data and decompressing it.
702 * the string to decode
703 * @return the decoded data
706 public static byte[] decode(String s) {
709 bytes = s.getBytes(PREFERRED_ENCODING);
711 catch (java.io.UnsupportedEncodingException uee) {
712 bytes = s.getBytes();
717 bytes = decode(bytes, 0, bytes.length);
719 // Check to see if it's gzip-compressed
720 // GZIP Magic Two-Byte Number: 0x8b1f (35615)
721 if (bytes != null && bytes.length >= 4) {
723 int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
724 if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
725 java.io.ByteArrayInputStream bais = null;
726 java.util.zip.GZIPInputStream gzis = null;
727 java.io.ByteArrayOutputStream baos = null;
728 byte[] buffer = new byte[2048];
732 baos = new java.io.ByteArrayOutputStream();
733 bais = new java.io.ByteArrayInputStream(bytes);
734 gzis = new java.util.zip.GZIPInputStream(bais);
736 while ((length = gzis.read(buffer)) >= 0) {
737 baos.write(buffer, 0, length);
738 } // end while: reading input
740 // No error? Get new bytes.
741 bytes = baos.toByteArray();
744 catch (java.io.IOException e) {
745 // Just return originally-decoded bytes
750 } catch (Exception e) {
754 } catch (Exception e) {
758 } catch (Exception e) {
763 } // end if: bytes.length >= 2
769 * Attempts to decode Base64 data and deserialize a Java Object within.
770 * Returns <tt>null</tt> if there was an error.
772 * @param encodedObject
773 * The Base64 data to decode
774 * @return The decoded and deserialized object
777 public static Object decodeToObject(String encodedObject) {
778 // Decode and gunzip if necessary
779 byte[] objBytes = decode(encodedObject);
781 java.io.ByteArrayInputStream bais = null;
782 java.io.ObjectInputStream ois = null;
786 bais = new java.io.ByteArrayInputStream(objBytes);
787 ois = new java.io.ObjectInputStream(bais);
789 obj = ois.readObject();
791 catch (java.io.IOException e) {
795 catch (java.lang.ClassNotFoundException e) {
802 } catch (Exception e) {
806 } catch (Exception e) {
811 } // end decodeObject
814 * Convenience method for encoding data to a file.
816 * @param dataToEncode
817 * byte array of data to encode in base64 form
819 * Filename for saving encoded data
820 * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
824 public static boolean encodeToFile(byte[] dataToEncode, String filename) {
825 boolean success = false;
826 Base64.OutputStream bos = null;
828 bos = new Base64.OutputStream(
829 new java.io.FileOutputStream(filename), Base64.ENCODE);
830 bos.write(dataToEncode);
833 catch (java.io.IOException e) {
836 } // end catch: IOException
840 } catch (Exception e) {
845 } // end encodeToFile
848 * Convenience method for decoding data to a file.
850 * @param dataToDecode
851 * Base64-encoded data as a string
853 * Filename for saving decoded data
854 * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
858 public static boolean decodeToFile(String dataToDecode, String filename) {
859 boolean success = false;
860 Base64.OutputStream bos = null;
862 bos = new Base64.OutputStream(
863 new java.io.FileOutputStream(filename), Base64.DECODE);
864 bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
867 catch (java.io.IOException e) {
869 } // end catch: IOException
873 } catch (Exception e) {
878 } // end decodeToFile
881 * Convenience method for reading a base64-encoded file and decoding it.
884 * Filename for reading encoded data
885 * @return decoded byte array or null if unsuccessful
889 public static byte[] decodeFromFile(String filename) {
890 byte[] decodedData = null;
891 Base64.InputStream bis = null;
893 // Set up some useful variables
894 java.io.File file = new java.io.File(filename);
895 byte[] buffer = null;
899 // Check for size of file
900 if (file.length() > Integer.MAX_VALUE) {
902 .println("File is too big for this convenience method ("
903 + file.length() + " bytes).");
905 } // end if: file too big for int index
906 buffer = new byte[(int) file.length()];
909 bis = new Base64.InputStream(new java.io.BufferedInputStream(
910 new java.io.FileInputStream(file)), Base64.DECODE);
913 while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
916 // Save in a variable to return
917 decodedData = new byte[length];
918 System.arraycopy(buffer, 0, decodedData, 0, length);
921 catch (java.io.IOException e) {
922 System.err.println("Error decoding from file " + filename);
923 } // end catch: IOException
927 } catch (Exception e) {
932 } // end decodeFromFile
935 * Convenience method for reading a binary file and base64-encoding it.
938 * Filename for reading binary data
939 * @return base64-encoded string or null if unsuccessful
943 public static String encodeFromFile(String filename) {
944 String encodedData = null;
945 Base64.InputStream bis = null;
947 // Set up some useful variables
948 java.io.File file = new java.io.File(filename);
949 byte[] buffer = new byte[(int) (file.length() * 1.4)];
954 bis = new Base64.InputStream(new java.io.BufferedInputStream(
955 new java.io.FileInputStream(file)), Base64.ENCODE);
958 while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
961 // Save in a variable to return
962 encodedData = new String(buffer, 0, length,
963 Base64.PREFERRED_ENCODING);
966 catch (java.io.IOException e) {
967 System.err.println("Error encoding from file " + filename);
968 } // end catch: IOException
972 } catch (Exception e) {
977 } // end encodeFromFile
979 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
982 * A {@link Base64.InputStream} will read data from another
983 * <tt>java.io.InputStream</tt>, given in the constructor, and
984 * encode/decode to/from Base64 notation on the fly.
989 public static class InputStream extends java.io.FilterInputStream {
990 private boolean encode; // Encoding or decoding
992 private int position; // Current position in the buffer
994 private byte[] buffer; // Small buffer holding converted data
996 private int bufferLength; // Length of buffer (3 or 4)
998 private int numSigBytes; // Number of meaningful bytes in the buffer
1000 private int lineLength;
1002 private boolean breakLines; // Break lines at less than 80 characters
1005 * Constructs a {@link Base64.InputStream} in DECODE mode.
1008 * the <tt>java.io.InputStream</tt> from which to read
1012 public InputStream(java.io.InputStream in) {
1014 } // end constructor
1017 * Constructs a {@link Base64.InputStream} in either ENCODE or DECODE
1023 * ENCODE or DECODE: Encode or Decode as data is read.
1024 * DONT_BREAK_LINES: don't break lines at 76 characters
1025 * (only meaningful when encoding)
1026 * <i>Note: Technically, this makes your encoding non-compliant.</i>
1030 * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1034 * the <tt>java.io.InputStream</tt> from which to read
1038 * @see Base64#ENCODE
1039 * @see Base64#DECODE
1040 * @see Base64#DONT_BREAK_LINES
1043 public InputStream(java.io.InputStream in, int options) {
1045 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1046 this.encode = (options & ENCODE) == ENCODE;
1047 this.bufferLength = encode ? 4 : 3;
1048 this.buffer = new byte[bufferLength];
1050 this.lineLength = 0;
1051 } // end constructor
1054 * Reads enough of the input stream to convert to/from Base64 and
1055 * returns the next byte.
1060 public int read() throws java.io.IOException {
1061 // Do we need to get data?
1064 byte[] b3 = new byte[3];
1065 int numBinaryBytes = 0;
1066 for (int i = 0; i < 3; i++) {
1070 // If end of stream, b is -1.
1074 } // end if: not end of stream
1077 catch (java.io.IOException e) {
1078 // Only a problem if we got no data at all.
1083 } // end for: each needed input byte
1085 if (numBinaryBytes > 0) {
1086 encode3to4(b3, 0, numBinaryBytes, buffer, 0);
1089 } // end if: got data
1093 } // end if: encoding
1097 byte[] b4 = new byte[4];
1099 for (i = 0; i < 4; i++) {
1100 // Read four "meaningful" bytes:
1105 && DECODABET[b & 0x7f] <= WHITE_SPACE_ENC);
1108 break; // Reads a -1 if end of stream
1111 } // end for: each needed input byte
1114 numSigBytes = decode4to3(b4, 0, buffer, 0);
1116 } // end if: got four characters
1119 } // end else if: also padded correctly
1121 // Must have broken out from above.
1122 throw new java.io.IOException(
1123 "Improperly padded Base64 input.");
1126 } // end else: decode
1127 } // end else: get data
1130 if (position >= 0) {
1131 // End of relevant data?
1132 if ( /* !encode && */position >= numSigBytes)
1135 if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
1140 lineLength++; // This isn't important when decoding
1141 // but throwing an extra "if" seems
1142 // just as wasteful.
1144 int b = buffer[position++];
1146 if (position >= bufferLength)
1149 return b & 0xFF; // This is how you "cast" a byte that's
1150 // intended to be unsigned.
1152 } // end if: position >= 0
1156 // When JDK1.4 is more accepted, use an assertion here.
1157 throw new java.io.IOException(
1158 "Error in Base64 code reading stream.");
1163 * Calls {@link #read()} repeatedly until the end of stream is reached
1164 * or <var>len</var> bytes are read. Returns number of bytes read into
1165 * array or -1 if end of stream is encountered.
1168 * array to hold values
1172 * max number of bytes to read into array
1173 * @return bytes read into array or -1 if end of stream is encountered.
1176 public int read(byte[] dest, int off, int len)
1177 throws java.io.IOException {
1180 for (i = 0; i < len; i++) {
1183 // if( b < 0 && i == 0 )
1187 dest[off + i] = (byte) b;
1191 break; // Out of 'for' loop
1192 } // end for: each byte read
1196 } // end inner class InputStream
1198 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1201 * A {@link Base64.OutputStream} will write data to another
1202 * <tt>java.io.OutputStream</tt>, given in the constructor, and
1203 * encode/decode to/from Base64 notation on the fly.
1208 public static class OutputStream extends java.io.FilterOutputStream {
1209 private boolean encode;
1211 private int position;
1213 private byte[] buffer;
1215 private int bufferLength;
1217 private int lineLength;
1219 private boolean breakLines;
1221 private byte[] b4; // Scratch used in a few places
1223 private boolean suspendEncoding;
1226 * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1229 * the <tt>java.io.OutputStream</tt> to which data will be
1233 public OutputStream(java.io.OutputStream out) {
1235 } // end constructor
1238 * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE
1244 * ENCODE or DECODE: Encode or Decode as data is read.
1245 * DONT_BREAK_LINES: don't break lines at 76 characters
1246 * (only meaningful when encoding)
1247 * <i>Note: Technically, this makes your encoding non-compliant.</i>
1251 * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1254 * the <tt>java.io.OutputStream</tt> to which data will be
1257 * Specified options.
1258 * @see Base64#ENCODE
1259 * @see Base64#DECODE
1260 * @see Base64#DONT_BREAK_LINES
1263 public OutputStream(java.io.OutputStream out, int options) {
1265 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1266 this.encode = (options & ENCODE) == ENCODE;
1267 this.bufferLength = encode ? 3 : 4;
1268 this.buffer = new byte[bufferLength];
1270 this.lineLength = 0;
1271 this.suspendEncoding = false;
1272 this.b4 = new byte[4];
1273 } // end constructor
1276 * Writes the byte to the output stream after converting to/from Base64
1277 * notation. When encoding, bytes are buffered three at a time before
1278 * the output stream actually gets a write() call. When decoding, bytes
1279 * are buffered four at a time.
1285 public void write(int theByte) throws java.io.IOException {
1286 // Encoding suspended?
1287 if (suspendEncoding) {
1288 super.out.write(theByte);
1290 } // end if: supsended
1294 buffer[position++] = (byte) theByte;
1295 if (position >= bufferLength) // Enough to encode.
1297 out.write(encode3to4(b4, buffer, bufferLength));
1300 if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1301 out.write(NEW_LINE);
1303 } // end if: end of line
1306 } // end if: enough to output
1307 } // end if: encoding
1311 // Meaningful Base64 character?
1312 if (DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC) {
1313 buffer[position++] = (byte) theByte;
1314 if (position >= bufferLength) // Enough to output.
1316 int len = Base64.decode4to3(buffer, 0, b4, 0);
1317 out.write(b4, 0, len);
1318 // out.write( Base64.decode4to3( buffer ) );
1320 } // end if: enough to output
1321 } // end if: meaningful base64 character
1322 else if (DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC) {
1323 throw new java.io.IOException(
1324 "Invalid character in Base64 data.");
1325 } // end else: not white space either
1326 } // end else: decoding
1330 * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are
1334 * array from which to read bytes
1338 * max number of bytes to read into array
1341 public void write(byte[] theBytes, int off, int len)
1342 throws java.io.IOException {
1343 // Encoding suspended?
1344 if (suspendEncoding) {
1345 super.out.write(theBytes, off, len);
1347 } // end if: supsended
1349 for (int i = 0; i < len; i++) {
1350 write(theBytes[off + i]);
1351 } // end for: each byte written
1356 * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer
1357 * without closing the stream.
1359 public void flushBase64() throws java.io.IOException {
1362 out.write(encode3to4(b4, buffer, position));
1364 } // end if: encoding
1366 throw new java.io.IOException(
1367 "Base64 input not properly padded.");
1368 } // end else: decoding
1369 } // end if: buffer partially full
1374 * Flushes and closes (I think, in the superclass) the stream.
1378 public void close() throws java.io.IOException {
1379 // 1. Ensure that pending characters are written
1382 // 2. Actually close the stream
1383 // Base class both flushes and closes.
1391 * Suspends encoding of the stream. May be helpful if you need to embed
1392 * a piece of base640-encoded data in a stream.
1396 public void suspendEncoding() throws java.io.IOException {
1398 this.suspendEncoding = true;
1399 } // end suspendEncoding
1402 * Resumes encoding of the stream. May be helpful if you need to embed a
1403 * piece of base640-encoded data in a stream.
1407 public void resumeEncoding() {
1408 this.suspendEncoding = false;
1409 } // end resumeEncoding
1411 } // end inner class OutputStream
1413 } // end class Base64