0001: /**
0002: * <p>
0003: * Change Log:
0004: * </p>
0005: * <ul>
0006: * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
0007: * some convenience methods for reading and writing to and from files.</li>
0008: * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
0009: * with other encodings (like EBCDIC).</li>
0010: * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
0011: * encoded data was a single byte.</li>
0012: * <li>v2.0 - I got rid of methods that used booleans to set options.
0013: * Now everything is more consolidated and cleaner. The code now detects
0014: * when data that's being decoded is gzip-compressed and will decompress it
0015: * automatically. Generally things are cleaner. You'll probably have to
0016: * change some method calls that you were making to support the new
0017: * options format (<tt>int</tt>s that you "OR" together).</li>
0018: * <li>v1.5.1 - Fixed bug when decompressing and decoding to a
0019: * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
0020: * Added the ability to "suspend" encoding in the Output Stream so
0021: * you can turn on and off the encoding if you need to embed base64
0022: * data in an otherwise "normal" stream (like an XML file).</li>
0023: * <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
0024: * This helps when using GZIP streams.
0025: * Added the ability to GZip-compress objects before encoding them.</li>
0026: * <li>v1.4 - Added helper methods to read/write files.</li>
0027: * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
0028: * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
0029: * where last buffer being read, if not completely full, was not returned.</li>
0030: * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
0031: * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
0032: * </ul>
0033: *
0034: * <p>
0035: * I am placing this code in the Public Domain. Do with it as you will.
0036: * This software comes with no guarantees or warranties but with
0037: * plenty of well-wishing instead!
0038: * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
0039: * periodically to check for updates or to contribute improvements.
0040: * </p>
0041: *
0042: * @author Robert Harder
0043: * @author rob@iharder.net
0044: * @version 2.1
0045: */package com.rift.coad.lib.thirdparty.base64;
0046:
0047: public class Base64 {
0048:
0049: /* ******** P U B L I C F I E L D S ******** */
0050:
0051: /** No options specified. Value is zero. */
0052: public final static int NO_OPTIONS = 0;
0053:
0054: /** Specify encoding. */
0055: public final static int ENCODE = 1;
0056:
0057: /** Specify decoding. */
0058: public final static int DECODE = 0;
0059:
0060: /** Specify that data should be gzip-compressed. */
0061: public final static int GZIP = 2;
0062:
0063: /** Don't break lines when encoding (violates strict Base64 specification) */
0064: public final static int DONT_BREAK_LINES = 8;
0065:
0066: /* ******** P R I V A T E F I E L D S ******** */
0067:
0068: /** Maximum line length (76) of Base64 output. */
0069: private final static int MAX_LINE_LENGTH = 76;
0070:
0071: /** The equals sign (=) as a byte. */
0072: private final static byte EQUALS_SIGN = (byte) '=';
0073:
0074: /** The new line character (\n) as a byte. */
0075: private final static byte NEW_LINE = (byte) '\n';
0076:
0077: /** Preferred encoding. */
0078: private final static String PREFERRED_ENCODING = "UTF-8";
0079:
0080: /** The 64 valid Base64 values. */
0081: private final static byte[] ALPHABET;
0082: private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */
0083: { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E',
0084: (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J',
0085: (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O',
0086: (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
0087: (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y',
0088: (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
0089: (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i',
0090: (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
0091: (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
0092: (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
0093: (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2',
0094: (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
0095: (byte) '8', (byte) '9', (byte) '+', (byte) '/' };
0096:
0097: /** Determine which ALPHABET to use. */
0098: static {
0099: byte[] __bytes;
0100: try {
0101: __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
0102: .getBytes(PREFERRED_ENCODING);
0103: } // end try
0104: catch (java.io.UnsupportedEncodingException use) {
0105: __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
0106: } // end catch
0107: ALPHABET = __bytes;
0108: } // end static
0109:
0110: /**
0111: * Translates a Base64 value to either its 6-bit reconstruction value
0112: * or a negative number indicating some other meaning.
0113: **/
0114: private final static byte[] DECODABET = { -9, -9, -9, -9, -9, -9,
0115: -9, -9, -9, // Decimal 0 - 8
0116: -5, -5, // Whitespace: Tab and Linefeed
0117: -9, -9, // Decimal 11 - 12
0118: -5, // Whitespace: Carriage Return
0119: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
0120: -9, -9, -9, -9, -9, // Decimal 27 - 31
0121: -5, // Whitespace: Space
0122: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
0123: 62, // Plus sign at decimal 43
0124: -9, -9, -9, // Decimal 44 - 46
0125: 63, // Slash at decimal 47
0126: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
0127: -9, -9, -9, // Decimal 58 - 60
0128: -1, // Equals sign at decimal 61
0129: -9, -9, -9, // Decimal 62 - 64
0130: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
0131: 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
0132: -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
0133: 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
0134: 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
0135: -9, -9, -9, -9 // Decimal 123 - 126
0136: /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
0137: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
0138: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
0139: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
0140: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
0141: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
0142: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
0143: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
0144: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
0145: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
0146: };
0147:
0148: // I think I end up not using the BAD_ENCODING indicator.
0149: //private final static byte BAD_ENCODING = -9; // Indicates error in encoding
0150: private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
0151: private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
0152:
0153: /** Defeats instantiation. */
0154: private Base64() {
0155: }
0156:
0157: /* ******** E N C O D I N G M E T H O D S ******** */
0158:
0159: /**
0160: * Encodes up to the first three bytes of array <var>threeBytes</var>
0161: * and returns a four-byte array in Base64 notation.
0162: * The actual number of significant bytes in your array is
0163: * given by <var>numSigBytes</var>.
0164: * The array <var>threeBytes</var> needs only be as big as
0165: * <var>numSigBytes</var>.
0166: * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
0167: *
0168: * @param b4 A reusable byte array to reduce array instantiation
0169: * @param threeBytes the array to convert
0170: * @param numSigBytes the number of significant bytes in your array
0171: * @return four byte array in Base64 notation.
0172: * @since 1.5.1
0173: */
0174: private static byte[] encode3to4(byte[] b4, byte[] threeBytes,
0175: int numSigBytes) {
0176: encode3to4(threeBytes, 0, numSigBytes, b4, 0);
0177: return b4;
0178: } // end encode3to4
0179:
0180: /**
0181: * Encodes up to three bytes of the array <var>source</var>
0182: * and writes the resulting four Base64 bytes to <var>destination</var>.
0183: * The source and destination arrays can be manipulated
0184: * anywhere along their length by specifying
0185: * <var>srcOffset</var> and <var>destOffset</var>.
0186: * This method does not check to make sure your arrays
0187: * are large enough to accomodate <var>srcOffset</var> + 3 for
0188: * the <var>source</var> array or <var>destOffset</var> + 4 for
0189: * the <var>destination</var> array.
0190: * The actual number of significant bytes in your array is
0191: * given by <var>numSigBytes</var>.
0192: *
0193: * @param source the array to convert
0194: * @param srcOffset the index where conversion begins
0195: * @param numSigBytes the number of significant bytes in your array
0196: * @param destination the array to hold the conversion
0197: * @param destOffset the index where output will be put
0198: * @return the <var>destination</var> array
0199: * @since 1.3
0200: */
0201: private static byte[] encode3to4(byte[] source, int srcOffset,
0202: int numSigBytes, byte[] destination, int destOffset) {
0203: // 1 2 3
0204: // 01234567890123456789012345678901 Bit position
0205: // --------000000001111111122222222 Array position from threeBytes
0206: // --------| || || || | Six bit groups to index ALPHABET
0207: // >>18 >>12 >> 6 >> 0 Right shift necessary
0208: // 0x3f 0x3f 0x3f Additional AND
0209:
0210: // Create buffer with zero-padding if there are only one or two
0211: // significant bytes passed in the array.
0212: // We have to shift left 24 in order to flush out the 1's that appear
0213: // when Java treats a value as negative that is cast from a byte to an int.
0214: int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8)
0215: : 0)
0216: | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16)
0217: : 0)
0218: | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24)
0219: : 0);
0220:
0221: switch (numSigBytes) {
0222: case 3:
0223: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
0224: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
0225: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
0226: destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
0227: return destination;
0228:
0229: case 2:
0230: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
0231: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
0232: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
0233: destination[destOffset + 3] = EQUALS_SIGN;
0234: return destination;
0235:
0236: case 1:
0237: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
0238: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
0239: destination[destOffset + 2] = EQUALS_SIGN;
0240: destination[destOffset + 3] = EQUALS_SIGN;
0241: return destination;
0242:
0243: default:
0244: return destination;
0245: } // end switch
0246: } // end encode3to4
0247:
0248: /**
0249: * Serializes an object and returns the Base64-encoded
0250: * version of that serialized object. If the object
0251: * cannot be serialized or there is another error,
0252: * the method will return <tt>null</tt>.
0253: * The object is not GZip-compressed before being encoded.
0254: *
0255: * @param serializableObject The object to encode
0256: * @return The Base64-encoded object
0257: * @since 1.4
0258: */
0259: public static String encodeObject(
0260: java.io.Serializable serializableObject) {
0261: return encodeObject(serializableObject, NO_OPTIONS);
0262: } // end encodeObject
0263:
0264: /**
0265: * Serializes an object and returns the Base64-encoded
0266: * version of that serialized object. If the object
0267: * cannot be serialized or there is another error,
0268: * the method will return <tt>null</tt>.
0269: * <p>
0270: * Valid options:<pre>
0271: * GZIP: gzip-compresses object before encoding it.
0272: * DONT_BREAK_LINES: don't break lines at 76 characters
0273: * <i>Note: Technically, this makes your encoding non-compliant.</i>
0274: * </pre>
0275: * <p>
0276: * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
0277: * <p>
0278: * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
0279: *
0280: * @param serializableObject The object to encode
0281: * @param options Specified options
0282: * @return The Base64-encoded object
0283: * @see Base64#GZIP
0284: * @see Base64#DONT_BREAK_LINES
0285: * @since 2.0
0286: */
0287: public static String encodeObject(
0288: java.io.Serializable serializableObject, int options) {
0289: // Streams
0290: java.io.ByteArrayOutputStream baos = null;
0291: java.io.OutputStream b64os = null;
0292: java.io.ObjectOutputStream oos = null;
0293: java.util.zip.GZIPOutputStream gzos = null;
0294:
0295: // Isolate options
0296: int gzip = (options & GZIP);
0297: int dontBreakLines = (options & DONT_BREAK_LINES);
0298:
0299: try {
0300: // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
0301: baos = new java.io.ByteArrayOutputStream();
0302: b64os = new Base64.OutputStream(baos, ENCODE
0303: | dontBreakLines);
0304:
0305: // GZip?
0306: if (gzip == GZIP) {
0307: gzos = new java.util.zip.GZIPOutputStream(b64os);
0308: oos = new java.io.ObjectOutputStream(gzos);
0309: } // end if: gzip
0310: else
0311: oos = new java.io.ObjectOutputStream(b64os);
0312:
0313: oos.writeObject(serializableObject);
0314: } // end try
0315: catch (java.io.IOException e) {
0316: e.printStackTrace();
0317: return null;
0318: } // end catch
0319: finally {
0320: try {
0321: oos.close();
0322: } catch (Exception e) {
0323: }
0324: try {
0325: gzos.close();
0326: } catch (Exception e) {
0327: }
0328: try {
0329: b64os.close();
0330: } catch (Exception e) {
0331: }
0332: try {
0333: baos.close();
0334: } catch (Exception e) {
0335: }
0336: } // end finally
0337:
0338: // Return value according to relevant encoding.
0339: try {
0340: return new String(baos.toByteArray(), PREFERRED_ENCODING);
0341: } // end try
0342: catch (java.io.UnsupportedEncodingException uue) {
0343: return new String(baos.toByteArray());
0344: } // end catch
0345:
0346: } // end encode
0347:
0348: /**
0349: * Encodes a byte array into Base64 notation.
0350: * Does not GZip-compress data.
0351: *
0352: * @param source The data to convert
0353: * @since 1.4
0354: */
0355: public static String encodeBytes(byte[] source) {
0356: return encodeBytes(source, 0, source.length, NO_OPTIONS);
0357: } // end encodeBytes
0358:
0359: /**
0360: * Encodes a byte array into Base64 notation.
0361: * <p>
0362: * Valid options:<pre>
0363: * GZIP: gzip-compresses object before encoding it.
0364: * DONT_BREAK_LINES: don't break lines at 76 characters
0365: * <i>Note: Technically, this makes your encoding non-compliant.</i>
0366: * </pre>
0367: * <p>
0368: * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
0369: * <p>
0370: * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
0371: *
0372: *
0373: * @param source The data to convert
0374: * @param options Specified options
0375: * @see Base64#GZIP
0376: * @see Base64#DONT_BREAK_LINES
0377: * @since 2.0
0378: */
0379: public static String encodeBytes(byte[] source, int options) {
0380: return encodeBytes(source, 0, source.length, options);
0381: } // end encodeBytes
0382:
0383: /**
0384: * Encodes a byte array into Base64 notation.
0385: * Does not GZip-compress data.
0386: *
0387: * @param source The data to convert
0388: * @param off Offset in array where conversion should begin
0389: * @param len Length of data to convert
0390: * @since 1.4
0391: */
0392: public static String encodeBytes(byte[] source, int off, int len) {
0393: return encodeBytes(source, off, len, NO_OPTIONS);
0394: } // end encodeBytes
0395:
0396: /**
0397: * Encodes a byte array into Base64 notation.
0398: * <p>
0399: * Valid options:<pre>
0400: * GZIP: gzip-compresses object before encoding it.
0401: * DONT_BREAK_LINES: don't break lines at 76 characters
0402: * <i>Note: Technically, this makes your encoding non-compliant.</i>
0403: * </pre>
0404: * <p>
0405: * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
0406: * <p>
0407: * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
0408: *
0409: *
0410: * @param source The data to convert
0411: * @param off Offset in array where conversion should begin
0412: * @param len Length of data to convert
0413: * @param options Specified options
0414: * @see Base64#GZIP
0415: * @see Base64#DONT_BREAK_LINES
0416: * @since 2.0
0417: */
0418: public static String encodeBytes(byte[] source, int off, int len,
0419: int options) {
0420: // Isolate options
0421: int dontBreakLines = (options & DONT_BREAK_LINES);
0422: int gzip = (options & GZIP);
0423:
0424: // Compress?
0425: if (gzip == GZIP) {
0426: java.io.ByteArrayOutputStream baos = null;
0427: java.util.zip.GZIPOutputStream gzos = null;
0428: Base64.OutputStream b64os = null;
0429:
0430: try {
0431: // GZip -> Base64 -> ByteArray
0432: baos = new java.io.ByteArrayOutputStream();
0433: b64os = new Base64.OutputStream(baos, ENCODE
0434: | dontBreakLines);
0435: gzos = new java.util.zip.GZIPOutputStream(b64os);
0436:
0437: gzos.write(source, off, len);
0438: gzos.close();
0439: } // end try
0440: catch (java.io.IOException e) {
0441: e.printStackTrace();
0442: return null;
0443: } // end catch
0444: finally {
0445: try {
0446: gzos.close();
0447: } catch (Exception e) {
0448: }
0449: try {
0450: b64os.close();
0451: } catch (Exception e) {
0452: }
0453: try {
0454: baos.close();
0455: } catch (Exception e) {
0456: }
0457: } // end finally
0458:
0459: // Return value according to relevant encoding.
0460: try {
0461: return new String(baos.toByteArray(),
0462: PREFERRED_ENCODING);
0463: } // end try
0464: catch (java.io.UnsupportedEncodingException uue) {
0465: return new String(baos.toByteArray());
0466: } // end catch
0467: } // end if: compress
0468:
0469: // Else, don't compress. Better not to use streams at all then.
0470: else {
0471: // Convert option to boolean in way that code likes it.
0472: boolean breakLines = dontBreakLines == 0;
0473:
0474: int len43 = len * 4 / 3;
0475: byte[] outBuff = new byte[(len43) // Main 4:3
0476: + ((len % 3) > 0 ? 4 : 0) // Account for padding
0477: + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
0478: int d = 0;
0479: int e = 0;
0480: int len2 = len - 2;
0481: int lineLength = 0;
0482: for (; d < len2; d += 3, e += 4) {
0483: encode3to4(source, d + off, 3, outBuff, e);
0484:
0485: lineLength += 4;
0486: if (breakLines && lineLength == MAX_LINE_LENGTH) {
0487: outBuff[e + 4] = NEW_LINE;
0488: e++;
0489: lineLength = 0;
0490: } // end if: end of line
0491: } // en dfor: each piece of array
0492:
0493: if (d < len) {
0494: encode3to4(source, d + off, len - d, outBuff, e);
0495: e += 4;
0496: } // end if: some padding needed
0497:
0498: // Return value according to relevant encoding.
0499: try {
0500: return new String(outBuff, 0, e, PREFERRED_ENCODING);
0501: } // end try
0502: catch (java.io.UnsupportedEncodingException uue) {
0503: return new String(outBuff, 0, e);
0504: } // end catch
0505:
0506: } // end else: don't compress
0507:
0508: } // end encodeBytes
0509:
0510: /* ******** D E C O D I N G M E T H O D S ******** */
0511:
0512: /**
0513: * Decodes four bytes from array <var>source</var>
0514: * and writes the resulting bytes (up to three of them)
0515: * to <var>destination</var>.
0516: * The source and destination arrays can be manipulated
0517: * anywhere along their length by specifying
0518: * <var>srcOffset</var> and <var>destOffset</var>.
0519: * This method does not check to make sure your arrays
0520: * are large enough to accomodate <var>srcOffset</var> + 4 for
0521: * the <var>source</var> array or <var>destOffset</var> + 3 for
0522: * the <var>destination</var> array.
0523: * This method returns the actual number of bytes that
0524: * were converted from the Base64 encoding.
0525: *
0526: *
0527: * @param source the array to convert
0528: * @param srcOffset the index where conversion begins
0529: * @param destination the array to hold the conversion
0530: * @param destOffset the index where output will be put
0531: * @return the number of decoded bytes converted
0532: * @since 1.3
0533: */
0534: private static int decode4to3(byte[] source, int srcOffset,
0535: byte[] destination, int destOffset) {
0536: // Example: Dk==
0537: if (source[srcOffset + 2] == EQUALS_SIGN) {
0538: // Two ways to do the same thing. Don't know which way I like best.
0539: //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
0540: // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
0541: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
0542: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
0543:
0544: destination[destOffset] = (byte) (outBuff >>> 16);
0545: return 1;
0546: }
0547:
0548: // Example: DkL=
0549: else if (source[srcOffset + 3] == EQUALS_SIGN) {
0550: // Two ways to do the same thing. Don't know which way I like best.
0551: //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
0552: // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
0553: // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
0554: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
0555: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
0556: | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
0557:
0558: destination[destOffset] = (byte) (outBuff >>> 16);
0559: destination[destOffset + 1] = (byte) (outBuff >>> 8);
0560: return 2;
0561: }
0562:
0563: // Example: DkLE
0564: else {
0565: try {
0566: // Two ways to do the same thing. Don't know which way I like best.
0567: //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
0568: // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
0569: // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
0570: // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
0571: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
0572: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
0573: | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
0574: | ((DECODABET[source[srcOffset + 3]] & 0xFF));
0575:
0576: destination[destOffset] = (byte) (outBuff >> 16);
0577: destination[destOffset + 1] = (byte) (outBuff >> 8);
0578: destination[destOffset + 2] = (byte) (outBuff);
0579:
0580: return 3;
0581: } catch (Exception e) {
0582: System.out.println("" + source[srcOffset] + ": "
0583: + (DECODABET[source[srcOffset]]));
0584: System.out.println("" + source[srcOffset + 1] + ": "
0585: + (DECODABET[source[srcOffset + 1]]));
0586: System.out.println("" + source[srcOffset + 2] + ": "
0587: + (DECODABET[source[srcOffset + 2]]));
0588: System.out.println("" + source[srcOffset + 3] + ": "
0589: + (DECODABET[source[srcOffset + 3]]));
0590: return -1;
0591: } //e nd catch
0592: }
0593: } // end decodeToBytes
0594:
0595: /**
0596: * Very low-level access to decoding ASCII characters in
0597: * the form of a byte array. Does not support automatically
0598: * gunzipping or any other "fancy" features.
0599: *
0600: * @param source The Base64 encoded data
0601: * @param off The offset of where to begin decoding
0602: * @param len The length of characters to decode
0603: * @return decoded data
0604: * @since 1.3
0605: */
0606: public static byte[] decode(byte[] source, int off, int len) {
0607: int len34 = len * 3 / 4;
0608: byte[] outBuff = new byte[len34]; // Upper limit on size of output
0609: int outBuffPosn = 0;
0610:
0611: byte[] b4 = new byte[4];
0612: int b4Posn = 0;
0613: int i = 0;
0614: byte sbiCrop = 0;
0615: byte sbiDecode = 0;
0616: for (i = off; i < off + len; i++) {
0617: sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
0618: sbiDecode = DECODABET[sbiCrop];
0619:
0620: if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better
0621: {
0622: if (sbiDecode >= EQUALS_SIGN_ENC) {
0623: b4[b4Posn++] = sbiCrop;
0624: if (b4Posn > 3) {
0625: outBuffPosn += decode4to3(b4, 0, outBuff,
0626: outBuffPosn);
0627: b4Posn = 0;
0628:
0629: // If that was the equals sign, break out of 'for' loop
0630: if (sbiCrop == EQUALS_SIGN)
0631: break;
0632: } // end if: quartet built
0633:
0634: } // end if: equals sign or better
0635:
0636: } // end if: white space, equals sign or better
0637: else {
0638: System.err.println("Bad Base64 input character at " + i
0639: + ": " + source[i] + "(decimal)");
0640: return null;
0641: } // end else:
0642: } // each input character
0643:
0644: byte[] out = new byte[outBuffPosn];
0645: System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
0646: return out;
0647: } // end decode
0648:
0649: /**
0650: * Decodes data from Base64 notation, automatically
0651: * detecting gzip-compressed data and decompressing it.
0652: *
0653: * @param s the string to decode
0654: * @return the decoded data
0655: * @since 1.4
0656: */
0657: public static byte[] decode(String s) {
0658: byte[] bytes;
0659: try {
0660: bytes = s.getBytes(PREFERRED_ENCODING);
0661: } // end try
0662: catch (java.io.UnsupportedEncodingException uee) {
0663: bytes = s.getBytes();
0664: } // end catch
0665: //</change>
0666:
0667: // Decode
0668: bytes = decode(bytes, 0, bytes.length);
0669:
0670: // Check to see if it's gzip-compressed
0671: // GZIP Magic Two-Byte Number: 0x8b1f (35615)
0672: if (bytes != null && bytes.length >= 4) {
0673:
0674: int head = ((int) bytes[0] & 0xff)
0675: | ((bytes[1] << 8) & 0xff00);
0676: if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
0677: java.io.ByteArrayInputStream bais = null;
0678: java.util.zip.GZIPInputStream gzis = null;
0679: java.io.ByteArrayOutputStream baos = null;
0680: byte[] buffer = new byte[2048];
0681: int length = 0;
0682:
0683: try {
0684: baos = new java.io.ByteArrayOutputStream();
0685: bais = new java.io.ByteArrayInputStream(bytes);
0686: gzis = new java.util.zip.GZIPInputStream(bais);
0687:
0688: while ((length = gzis.read(buffer)) >= 0) {
0689: baos.write(buffer, 0, length);
0690: } // end while: reading input
0691:
0692: // No error? Get new bytes.
0693: bytes = baos.toByteArray();
0694:
0695: } // end try
0696: catch (java.io.IOException e) {
0697: // Just return originally-decoded bytes
0698: } // end catch
0699: finally {
0700: try {
0701: baos.close();
0702: } catch (Exception e) {
0703: }
0704: try {
0705: gzis.close();
0706: } catch (Exception e) {
0707: }
0708: try {
0709: bais.close();
0710: } catch (Exception e) {
0711: }
0712: } // end finally
0713:
0714: } // end if: gzipped
0715: } // end if: bytes.length >= 2
0716:
0717: return bytes;
0718: } // end decode
0719:
0720: /**
0721: * Attempts to decode Base64 data and deserialize a Java
0722: * Object within. Returns <tt>null</tt> if there was an error.
0723: *
0724: * @param encodedObject The Base64 data to decode
0725: * @return The decoded and deserialized object
0726: * @since 1.5
0727: */
0728: public static Object decodeToObject(String encodedObject) {
0729: // Decode and gunzip if necessary
0730: byte[] objBytes = decode(encodedObject);
0731:
0732: java.io.ByteArrayInputStream bais = null;
0733: java.io.ObjectInputStream ois = null;
0734: Object obj = null;
0735:
0736: try {
0737: bais = new java.io.ByteArrayInputStream(objBytes);
0738: ois = new java.io.ObjectInputStream(bais);
0739:
0740: obj = ois.readObject();
0741: } // end try
0742: catch (java.io.IOException e) {
0743: e.printStackTrace();
0744: obj = null;
0745: } // end catch
0746: catch (java.lang.ClassNotFoundException e) {
0747: e.printStackTrace();
0748: obj = null;
0749: } // end catch
0750: finally {
0751: try {
0752: bais.close();
0753: } catch (Exception e) {
0754: }
0755: try {
0756: ois.close();
0757: } catch (Exception e) {
0758: }
0759: } // end finally
0760:
0761: return obj;
0762: } // end decodeObject
0763:
0764: /**
0765: * Convenience method for encoding data to a file.
0766: *
0767: * @param dataToEncode byte array of data to encode in base64 form
0768: * @param filename Filename for saving encoded data
0769: * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
0770: *
0771: * @since 2.1
0772: */
0773: public static boolean encodeToFile(byte[] dataToEncode,
0774: String filename) {
0775: boolean success = false;
0776: Base64.OutputStream bos = null;
0777: try {
0778: bos = new Base64.OutputStream(new java.io.FileOutputStream(
0779: filename), Base64.ENCODE);
0780: bos.write(dataToEncode);
0781: success = true;
0782: } // end try
0783: catch (java.io.IOException e) {
0784:
0785: success = false;
0786: } // end catch: IOException
0787: finally {
0788: try {
0789: bos.close();
0790: } catch (Exception e) {
0791: }
0792: } // end finally
0793:
0794: return success;
0795: } // end encodeToFile
0796:
0797: /**
0798: * Convenience method for decoding data to a file.
0799: *
0800: * @param dataToDecode Base64-encoded data as a string
0801: * @param filename Filename for saving decoded data
0802: * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
0803: *
0804: * @since 2.1
0805: */
0806: public static boolean decodeToFile(String dataToDecode,
0807: String filename) {
0808: boolean success = false;
0809: Base64.OutputStream bos = null;
0810: try {
0811: bos = new Base64.OutputStream(new java.io.FileOutputStream(
0812: filename), Base64.DECODE);
0813: bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
0814: success = true;
0815: } // end try
0816: catch (java.io.IOException e) {
0817: success = false;
0818: } // end catch: IOException
0819: finally {
0820: try {
0821: bos.close();
0822: } catch (Exception e) {
0823: }
0824: } // end finally
0825:
0826: return success;
0827: } // end decodeToFile
0828:
0829: /**
0830: * Convenience method for reading a base64-encoded
0831: * file and decoding it.
0832: *
0833: * @param filename Filename for reading encoded data
0834: * @return decoded byte array or null if unsuccessful
0835: *
0836: * @since 2.1
0837: */
0838: public static byte[] decodeFromFile(String filename) {
0839: byte[] decodedData = null;
0840: Base64.InputStream bis = null;
0841: try {
0842: // Set up some useful variables
0843: java.io.File file = new java.io.File(filename);
0844: byte[] buffer = null;
0845: int length = 0;
0846: int numBytes = 0;
0847:
0848: // Check for size of file
0849: if (file.length() > Integer.MAX_VALUE) {
0850: System.err
0851: .println("File is too big for this convenience method ("
0852: + file.length() + " bytes).");
0853: return null;
0854: } // end if: file too big for int index
0855: buffer = new byte[(int) file.length()];
0856:
0857: // Open a stream
0858: bis = new Base64.InputStream(
0859: new java.io.BufferedInputStream(
0860: new java.io.FileInputStream(file)),
0861: Base64.DECODE);
0862:
0863: // Read until done
0864: while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
0865: length += numBytes;
0866:
0867: // Save in a variable to return
0868: decodedData = new byte[length];
0869: System.arraycopy(buffer, 0, decodedData, 0, length);
0870:
0871: } // end try
0872: catch (java.io.IOException e) {
0873: System.err.println("Error decoding from file " + filename);
0874: } // end catch: IOException
0875: finally {
0876: try {
0877: bis.close();
0878: } catch (Exception e) {
0879: }
0880: } // end finally
0881:
0882: return decodedData;
0883: } // end decodeFromFile
0884:
0885: /**
0886: * Convenience method for reading a binary file
0887: * and base64-encoding it.
0888: *
0889: * @param filename Filename for reading binary data
0890: * @return base64-encoded string or null if unsuccessful
0891: *
0892: * @since 2.1
0893: */
0894: public static String encodeFromFile(String filename) {
0895: String encodedData = null;
0896: Base64.InputStream bis = null;
0897: try {
0898: // Set up some useful variables
0899: java.io.File file = new java.io.File(filename);
0900: byte[] buffer = new byte[(int) (file.length() * 1.4)];
0901: int length = 0;
0902: int numBytes = 0;
0903:
0904: // Open a stream
0905: bis = new Base64.InputStream(
0906: new java.io.BufferedInputStream(
0907: new java.io.FileInputStream(file)),
0908: Base64.ENCODE);
0909:
0910: // Read until done
0911: while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
0912: length += numBytes;
0913:
0914: // Save in a variable to return
0915: encodedData = new String(buffer, 0, length,
0916: Base64.PREFERRED_ENCODING);
0917:
0918: } // end try
0919: catch (java.io.IOException e) {
0920: System.err.println("Error encoding from file " + filename);
0921: } // end catch: IOException
0922: finally {
0923: try {
0924: bis.close();
0925: } catch (Exception e) {
0926: }
0927: } // end finally
0928:
0929: return encodedData;
0930: } // end encodeFromFile
0931:
0932: /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
0933:
0934: /**
0935: * A {@link Base64.InputStream} will read data from another
0936: * <tt>java.io.InputStream</tt>, given in the constructor,
0937: * and encode/decode to/from Base64 notation on the fly.
0938: *
0939: * @see Base64
0940: * @since 1.3
0941: */
0942: public static class InputStream extends java.io.FilterInputStream {
0943: private boolean encode; // Encoding or decoding
0944: private int position; // Current position in the buffer
0945: private byte[] buffer; // Small buffer holding converted data
0946: private int bufferLength; // Length of buffer (3 or 4)
0947: private int numSigBytes; // Number of meaningful bytes in the buffer
0948: private int lineLength;
0949: private boolean breakLines; // Break lines at less than 80 characters
0950:
0951: /**
0952: * Constructs a {@link Base64.InputStream} in DECODE mode.
0953: *
0954: * @param in the <tt>java.io.InputStream</tt> from which to read data.
0955: * @since 1.3
0956: */
0957: public InputStream(java.io.InputStream in) {
0958: this (in, DECODE);
0959: } // end constructor
0960:
0961: /**
0962: * Constructs a {@link Base64.InputStream} in
0963: * either ENCODE or DECODE mode.
0964: * <p>
0965: * Valid options:<pre>
0966: * ENCODE or DECODE: Encode or Decode as data is read.
0967: * DONT_BREAK_LINES: don't break lines at 76 characters
0968: * (only meaningful when encoding)
0969: * <i>Note: Technically, this makes your encoding non-compliant.</i>
0970: * </pre>
0971: * <p>
0972: * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
0973: *
0974: *
0975: * @param in the <tt>java.io.InputStream</tt> from which to read data.
0976: * @param options Specified options
0977: * @see Base64#ENCODE
0978: * @see Base64#DECODE
0979: * @see Base64#DONT_BREAK_LINES
0980: * @since 2.0
0981: */
0982: public InputStream(java.io.InputStream in, int options) {
0983: super (in);
0984: this .breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
0985: this .encode = (options & ENCODE) == ENCODE;
0986: this .bufferLength = encode ? 4 : 3;
0987: this .buffer = new byte[bufferLength];
0988: this .position = -1;
0989: this .lineLength = 0;
0990: } // end constructor
0991:
0992: /**
0993: * Reads enough of the input stream to convert
0994: * to/from Base64 and returns the next byte.
0995: *
0996: * @return next byte
0997: * @since 1.3
0998: */
0999: public int read() throws java.io.IOException {
1000: // Do we need to get data?
1001: if (position < 0) {
1002: if (encode) {
1003: byte[] b3 = new byte[3];
1004: int numBinaryBytes = 0;
1005: for (int i = 0; i < 3; i++) {
1006: try {
1007: int b = in.read();
1008:
1009: // If end of stream, b is -1.
1010: if (b >= 0) {
1011: b3[i] = (byte) b;
1012: numBinaryBytes++;
1013: } // end if: not end of stream
1014:
1015: } // end try: read
1016: catch (java.io.IOException e) {
1017: // Only a problem if we got no data at all.
1018: if (i == 0)
1019: throw e;
1020:
1021: } // end catch
1022: } // end for: each needed input byte
1023:
1024: if (numBinaryBytes > 0) {
1025: encode3to4(b3, 0, numBinaryBytes, buffer, 0);
1026: position = 0;
1027: numSigBytes = 4;
1028: } // end if: got data
1029: else {
1030: return -1;
1031: } // end else
1032: } // end if: encoding
1033:
1034: // Else decoding
1035: else {
1036: byte[] b4 = new byte[4];
1037: int i = 0;
1038: for (i = 0; i < 4; i++) {
1039: // Read four "meaningful" bytes:
1040: int b = 0;
1041: do {
1042: b = in.read();
1043: } while (b >= 0
1044: && DECODABET[b & 0x7f] <= WHITE_SPACE_ENC);
1045:
1046: if (b < 0)
1047: break; // Reads a -1 if end of stream
1048:
1049: b4[i] = (byte) b;
1050: } // end for: each needed input byte
1051:
1052: if (i == 4) {
1053: numSigBytes = decode4to3(b4, 0, buffer, 0);
1054: position = 0;
1055: } // end if: got four characters
1056: else if (i == 0) {
1057: return -1;
1058: } // end else if: also padded correctly
1059: else {
1060: // Must have broken out from above.
1061: throw new java.io.IOException(
1062: "Improperly padded Base64 input.");
1063: } // end
1064:
1065: } // end else: decode
1066: } // end else: get data
1067:
1068: // Got data?
1069: if (position >= 0) {
1070: // End of relevant data?
1071: if ( /*!encode &&*/position >= numSigBytes)
1072: return -1;
1073:
1074: if (encode && breakLines
1075: && lineLength >= MAX_LINE_LENGTH) {
1076: lineLength = 0;
1077: return '\n';
1078: } // end if
1079: else {
1080: lineLength++; // This isn't important when decoding
1081: // but throwing an extra "if" seems
1082: // just as wasteful.
1083:
1084: int b = buffer[position++];
1085:
1086: if (position >= bufferLength)
1087: position = -1;
1088:
1089: return b & 0xFF; // This is how you "cast" a byte that's
1090: // intended to be unsigned.
1091: } // end else
1092: } // end if: position >= 0
1093:
1094: // Else error
1095: else {
1096: // When JDK1.4 is more accepted, use an assertion here.
1097: throw new java.io.IOException(
1098: "Error in Base64 code reading stream.");
1099: } // end else
1100: } // end read
1101:
1102: /**
1103: * Calls {@link #read()} repeatedly until the end of stream
1104: * is reached or <var>len</var> bytes are read.
1105: * Returns number of bytes read into array or -1 if
1106: * end of stream is encountered.
1107: *
1108: * @param dest array to hold values
1109: * @param off offset for array
1110: * @param len max number of bytes to read into array
1111: * @return bytes read into array or -1 if end of stream is encountered.
1112: * @since 1.3
1113: */
1114: public int read(byte[] dest, int off, int len)
1115: throws java.io.IOException {
1116: int i;
1117: int b;
1118: for (i = 0; i < len; i++) {
1119: b = read();
1120:
1121: //if( b < 0 && i == 0 )
1122: // return -1;
1123:
1124: if (b >= 0)
1125: dest[off + i] = (byte) b;
1126: else if (i == 0)
1127: return -1;
1128: else
1129: break; // Out of 'for' loop
1130: } // end for: each byte read
1131: return i;
1132: } // end read
1133:
1134: } // end inner class InputStream
1135:
1136: /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1137:
1138: /**
1139: * A {@link Base64.OutputStream} will write data to another
1140: * <tt>java.io.OutputStream</tt>, given in the constructor,
1141: * and encode/decode to/from Base64 notation on the fly.
1142: *
1143: * @see Base64
1144: * @since 1.3
1145: */
1146: public static class OutputStream extends java.io.FilterOutputStream {
1147: private boolean encode;
1148: private int position;
1149: private byte[] buffer;
1150: private int bufferLength;
1151: private int lineLength;
1152: private boolean breakLines;
1153: private byte[] b4; // Scratch used in a few places
1154: private boolean suspendEncoding;
1155:
1156: /**
1157: * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1158: *
1159: * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1160: * @since 1.3
1161: */
1162: public OutputStream(java.io.OutputStream out) {
1163: this (out, ENCODE);
1164: } // end constructor
1165:
1166: /**
1167: * Constructs a {@link Base64.OutputStream} in
1168: * either ENCODE or DECODE mode.
1169: * <p>
1170: * Valid options:<pre>
1171: * ENCODE or DECODE: Encode or Decode as data is read.
1172: * DONT_BREAK_LINES: don't break lines at 76 characters
1173: * (only meaningful when encoding)
1174: * <i>Note: Technically, this makes your encoding non-compliant.</i>
1175: * </pre>
1176: * <p>
1177: * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1178: *
1179: * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1180: * @param options Specified options.
1181: * @see Base64#ENCODE
1182: * @see Base64#DECODE
1183: * @see Base64#DONT_BREAK_LINES
1184: * @since 1.3
1185: */
1186: public OutputStream(java.io.OutputStream out, int options) {
1187: super (out);
1188: this .breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1189: this .encode = (options & ENCODE) == ENCODE;
1190: this .bufferLength = encode ? 3 : 4;
1191: this .buffer = new byte[bufferLength];
1192: this .position = 0;
1193: this .lineLength = 0;
1194: this .suspendEncoding = false;
1195: this .b4 = new byte[4];
1196: } // end constructor
1197:
1198: /**
1199: * Writes the byte to the output stream after
1200: * converting to/from Base64 notation.
1201: * When encoding, bytes are buffered three
1202: * at a time before the output stream actually
1203: * gets a write() call.
1204: * When decoding, bytes are buffered four
1205: * at a time.
1206: *
1207: * @param theByte the byte to write
1208: * @since 1.3
1209: */
1210: public void write(int theByte) throws java.io.IOException {
1211: // Encoding suspended?
1212: if (suspendEncoding) {
1213: super .out.write(theByte);
1214: return;
1215: } // end if: supsended
1216:
1217: // Encode?
1218: if (encode) {
1219: buffer[position++] = (byte) theByte;
1220: if (position >= bufferLength) // Enough to encode.
1221: {
1222: out.write(encode3to4(b4, buffer, bufferLength));
1223:
1224: lineLength += 4;
1225: if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1226: out.write(NEW_LINE);
1227: lineLength = 0;
1228: } // end if: end of line
1229:
1230: position = 0;
1231: } // end if: enough to output
1232: } // end if: encoding
1233:
1234: // Else, Decoding
1235: else {
1236: // Meaningful Base64 character?
1237: if (DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC) {
1238: buffer[position++] = (byte) theByte;
1239: if (position >= bufferLength) // Enough to output.
1240: {
1241: int len = Base64.decode4to3(buffer, 0, b4, 0);
1242: out.write(b4, 0, len);
1243: //out.write( Base64.decode4to3( buffer ) );
1244: position = 0;
1245: } // end if: enough to output
1246: } // end if: meaningful base64 character
1247: else if (DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC) {
1248: throw new java.io.IOException(
1249: "Invalid character in Base64 data.");
1250: } // end else: not white space either
1251: } // end else: decoding
1252: } // end write
1253:
1254: /**
1255: * Calls {@link #write(int)} repeatedly until <var>len</var>
1256: * bytes are written.
1257: *
1258: * @param theBytes array from which to read bytes
1259: * @param off offset for array
1260: * @param len max number of bytes to read into array
1261: * @since 1.3
1262: */
1263: public void write(byte[] theBytes, int off, int len)
1264: throws java.io.IOException {
1265: // Encoding suspended?
1266: if (suspendEncoding) {
1267: super .out.write(theBytes, off, len);
1268: return;
1269: } // end if: supsended
1270:
1271: for (int i = 0; i < len; i++) {
1272: write(theBytes[off + i]);
1273: } // end for: each byte written
1274:
1275: } // end write
1276:
1277: /**
1278: * Method added by PHIL. [Thanks, PHIL. -Rob]
1279: * This pads the buffer without closing the stream.
1280: */
1281: public void flushBase64() throws java.io.IOException {
1282: if (position > 0) {
1283: if (encode) {
1284: out.write(encode3to4(b4, buffer, position));
1285: position = 0;
1286: } // end if: encoding
1287: else {
1288: throw new java.io.IOException(
1289: "Base64 input not properly padded.");
1290: } // end else: decoding
1291: } // end if: buffer partially full
1292:
1293: } // end flush
1294:
1295: /**
1296: * Flushes and closes (I think, in the superclass) the stream.
1297: *
1298: * @since 1.3
1299: */
1300: public void close() throws java.io.IOException {
1301: // 1. Ensure that pending characters are written
1302: flushBase64();
1303:
1304: // 2. Actually close the stream
1305: // Base class both flushes and closes.
1306: super .close();
1307:
1308: buffer = null;
1309: out = null;
1310: } // end close
1311:
1312: /**
1313: * Suspends encoding of the stream.
1314: * May be helpful if you need to embed a piece of
1315: * base640-encoded data in a stream.
1316: *
1317: * @since 1.5.1
1318: */
1319: public void suspendEncoding() throws java.io.IOException {
1320: flushBase64();
1321: this .suspendEncoding = true;
1322: } // end suspendEncoding
1323:
1324: /**
1325: * Resumes encoding of the stream.
1326: * May be helpful if you need to embed a piece of
1327: * base640-encoded data in a stream.
1328: *
1329: * @since 1.5.1
1330: */
1331: public void resumeEncoding() {
1332: this .suspendEncoding = false;
1333: } // end resumeEncoding
1334:
1335: } // end inner class OutputStream
1336:
1337: } // end class Base64
|