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