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