001: package org.dbunit.util;
002:
003: import org.slf4j.Logger;
004: import org.slf4j.LoggerFactory;
005:
006: /**
007: * <p>
008: * I am placing this code in the Public Domain. Do with it as you will.
009: * This software comes with no guarantees or warranties but with
010: * plenty of well-wishing instead!
011: * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
012: * periodically to check for updates or to contribute improvements.
013: * </p>
014: *
015: * @author Robert Harder
016: * @author rharder@usa.net
017: * @version 1.3
018: */
019: public class Base64 {
020:
021: /**
022: * Logger for this class
023: */
024: private static final Logger logger = LoggerFactory
025: .getLogger(Base64.class);
026:
027: /** Specify encoding (value is <tt>true</tt>). */
028: public final static boolean ENCODE = true;
029:
030: /** Specify decoding (value is <tt>false</tt>). */
031: public final static boolean DECODE = false;
032:
033: /** Maximum line length (76) of Base64 output. */
034: private final static int MAX_LINE_LENGTH = 76;
035:
036: /** The equals sign (=) as a byte. */
037: private final static byte EQUALS_SIGN = (byte) '=';
038:
039: /** The new line character (\n) as a byte. */
040: private final static byte NEW_LINE = (byte) '\n';
041:
042: /** The 64 valid Base64 values. */
043: private final static byte[] ALPHABET = { (byte) 'A', (byte) 'B',
044: (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
045: (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
046: (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
047: (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
048: (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
049: (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
050: (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
051: (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
052: (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
053: (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
054: (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
055: (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
056: (byte) '+', (byte) '/' };
057:
058: /**
059: * Translates a Base64 value to either its 6-bit reconstruction value
060: * or a negative number indicating some other meaning.
061: **/
062: private final static byte[] DECODABET = { -9, -9, -9, -9, -9, -9,
063: -9, -9, -9, // Decimal 0 - 8
064: -5, -5, // Whitespace: Tab and Linefeed
065: -9, -9, // Decimal 11 - 12
066: -5, // Whitespace: Carriage Return
067: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
068: -9, -9, -9, -9, -9, // Decimal 27 - 31
069: -5, // Whitespace: Space
070: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
071: 62, // Plus sign at decimal 43
072: -9, -9, -9, // Decimal 44 - 46
073: 63, // Slash at decimal 47
074: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
075: -9, -9, -9, // Decimal 58 - 60
076: -1, // Equals sign at decimal 61
077: -9, -9, -9, // Decimal 62 - 64
078: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
079: 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
080: -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
081: 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
082: 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
083: -9, -9, -9, -9 // Decimal 123 - 126
084: /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
085: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
086: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
087: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
088: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
089: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
090: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
091: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
092: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
093: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
094: };
095:
096: private final static byte BAD_ENCODING = -9; // Indicates error in encoding
097: private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
098: private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
099:
100: /** Defeats instantiation. */
101: private Base64() {
102: }
103:
104: /** Testing. */
105: public static void main(String[] args) {
106: logger.debug("main(args=" + args + ") - start");
107:
108: String s = "Hello, world";
109: s = "abcd";
110: //s = System.getProperties().toString();
111: //System.out.println( s + ": \n [" + encode( s ) + "]\n [" + decode(encode(s)) + "]" );
112:
113: byte[] b = encodeString(s).getBytes();
114: byte[] c = decode(b, 0, b.length);
115:
116: System.out.println("\n\n" + s + ":" + new String(b) + ":"
117: + new String(c));
118:
119: try {
120: java.io.FileInputStream fis = new java.io.FileInputStream(
121: "c:\\abcd.txt");
122: InputStream b64is = new InputStream(fis, DECODE);
123: int ib = 0;
124: while ((ib = b64is.read()) > 0) { //System.out.print( new String( ""+(char)ib ) );
125: }
126: } // end try
127: catch (Exception e) {
128: logger.error("main()", e);
129:
130: e.printStackTrace();
131: }
132: }
133:
134: /* ******** E N C O D I N G M E T H O D S ******** */
135:
136: /**
137: * Encodes the first three bytes of array <var>threeBytes</var>
138: * and returns a four-byte array in Base64 notation.
139: *
140: * @param threeBytes the array to convert
141: * @return four byte array in Base64 notation.
142: * @since 1.3
143: */
144: private static byte[] encode3to4(byte[] threeBytes) {
145: logger.debug("encode3to4(threeBytes=" + threeBytes
146: + ") - start");
147:
148: return encode3to4(threeBytes, 3);
149: } // end encodeToBytes
150:
151: /**
152: * Encodes up to the first three bytes of array <var>threeBytes</var>
153: * and returns a four-byte array in Base64 notation.
154: * The actual number of significant bytes in your array is
155: * given by <var>numSigBytes</var>.
156: * The array <var>threeBytes</var> needs only be as big as
157: * <var>numSigBytes</var>.
158: *
159: * @param threeBytes the array to convert
160: * @param numSigBytes the number of significant bytes in your array
161: * @return four byte array in Base64 notation.
162: * @since 1.3
163: */
164: private static byte[] encode3to4(byte[] threeBytes, int numSigBytes) {
165: logger.debug("encode3to4(threeBytes=" + threeBytes
166: + ", numSigBytes=" + numSigBytes + ") - start");
167:
168: byte[] dest = new byte[4];
169: encode3to4(threeBytes, 0, numSigBytes, dest, 0);
170: return dest;
171: }
172:
173: /**
174: * Encodes up to three bytes of the array <var>source</var>
175: * and writes the resulting four Base64 bytes to <var>destination</var>.
176: * The source and destination arrays can be manipulated
177: * anywhere along their length by specifying
178: * <var>srcOffset</var> and <var>destOffset</var>.
179: * This method does not check to make sure your arrays
180: * are large enough to accomodate <var>srcOffset</var> + 3 for
181: * the <var>source</var> array or <var>destOffset</var> + 4 for
182: * the <var>destination</var> array.
183: * The actual number of significant bytes in your array is
184: * given by <var>numSigBytes</var>.
185: *
186: * @param source the array to convert
187: * @param srcOffset the index where conversion begins
188: * @param numSigBytes the number of significant bytes in your array
189: * @param destination the array to hold the conversion
190: * @param destOffset the index where output will be put
191: * @return the <var>destination</var> array
192: * @since 1.3
193: */
194: private static byte[] encode3to4(byte[] source, int srcOffset,
195: int numSigBytes, byte[] destination, int destOffset) {
196: logger.debug("encode3to4(source=" + source + ", srcOffset="
197: + srcOffset + ", numSigBytes=" + numSigBytes
198: + ", destination=" + destination + ", destOffset="
199: + destOffset + ") - start");
200:
201: // 1 2 3
202: // 01234567890123456789012345678901 Bit position
203: // --------000000001111111122222222 Array position from threeBytes
204: // --------| || || || | Six bit groups to index ALPHABET
205: // >>18 >>12 >> 6 >> 0 Right shift necessary
206: // 0x3f 0x3f 0x3f Additional AND
207:
208: // Create buffer with zero-padding if there are only one or two
209: // significant bytes passed in the array.
210: // We have to shift left 24 in order to flush out the 1's that appear
211: // when Java treats a value as negative that is cast from a byte to an int.
212: int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8)
213: : 0)
214: | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16)
215: : 0)
216: | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24)
217: : 0);
218:
219: switch (numSigBytes) {
220: case 3:
221: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
222: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
223: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
224: destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
225: return destination;
226:
227: case 2:
228: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
229: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
230: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
231: destination[destOffset + 3] = EQUALS_SIGN;
232: return destination;
233:
234: case 1:
235: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
236: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
237: destination[destOffset + 2] = EQUALS_SIGN;
238: destination[destOffset + 3] = EQUALS_SIGN;
239: return destination;
240:
241: default:
242: return destination;
243: } // end switch
244: } // end encode3to4
245:
246: /**
247: * Serializes an object and returns the Base64-encoded
248: * version of that serialized object. If the object
249: * cannot be serialized or there is another error,
250: * the method will return <tt>null</tt>.
251: *
252: * @param serializableObject The object to encode
253: * @return The Base64-encoded object
254: * @since 1.4
255: */
256: public static String encodeObject(
257: java.io.Serializable serializableObject) {
258: logger.debug("encodeObject(serializableObject="
259: + serializableObject + ") - start");
260:
261: java.io.ByteArrayOutputStream baos = null;
262: java.io.OutputStream b64os = null;
263: java.io.ObjectOutputStream oos = null;
264:
265: try {
266: baos = new java.io.ByteArrayOutputStream();
267: b64os = new OutputStream(baos, Base64.ENCODE);
268: oos = new java.io.ObjectOutputStream(b64os);
269:
270: oos.writeObject(serializableObject);
271: } // end try
272: catch (java.io.IOException e) {
273: logger.error("encodeObject()", e);
274:
275: e.printStackTrace();
276: return null;
277: } // end catch
278: finally {
279: try {
280: oos.close();
281: } catch (Exception e) {
282: logger.error("encodeObject()", e);
283: }
284: try {
285: b64os.close();
286: } catch (Exception e) {
287: logger.error("encodeObject()", e);
288: }
289: try {
290: baos.close();
291: } catch (Exception e) {
292: logger.error("encodeObject()", e);
293: }
294: } // end finally
295:
296: return new String(baos.toByteArray());
297: } // end encode
298:
299: /**
300: * Encodes a byte array into Base64 notation.
301: * Equivalen to calling
302: * <code>encodeBytes( source, 0, source.length )</code>
303: *
304: * @param source The data to convert
305: * @since 1.4
306: */
307: public static String encodeBytes(byte[] source) {
308: logger.debug("encodeBytes(source=" + source + ") - start");
309:
310: return encodeBytes(source, 0, source.length);
311: } // end encodeBytes
312:
313: /**
314: * Encodes a byte array into Base64 notation.
315: *
316: * @param source The data to convert
317: * @param off Offset in array where conversion should begin
318: * @param len Length of data to convert
319: * @since 1.4
320: */
321: public static String encodeBytes(byte[] source, int off, int len) {
322: logger.debug("encodeBytes(source=" + source + ", off=" + off
323: + ", len=" + len + ") - start");
324:
325: int len43 = len * 4 / 3;
326: byte[] outBuff = new byte[(len43) // Main 4:3
327: + ((len % 3) > 0 ? 4 : 0) // Account for padding
328: + (len43 / MAX_LINE_LENGTH)]; // New lines
329: int d = 0;
330: int e = 0;
331: int len2 = len - 2;
332: int lineLength = 0;
333: for (; d < len2; d += 3, e += 4) {
334: encode3to4(source, d, 3, outBuff, e);
335:
336: lineLength += 4;
337: if (lineLength == MAX_LINE_LENGTH) {
338: outBuff[e + 4] = NEW_LINE;
339: e++;
340: lineLength = 0;
341: } // end if: end of line
342: } // en dfor: each piece of array
343:
344: if (d < len) {
345: encode3to4(source, d, len - d, outBuff, e);
346: e += 4;
347: } // end if: some padding needed
348:
349: return new String(outBuff, 0, e);
350: } // end encodeBytes
351:
352: /**
353: * Encodes a string in Base64 notation with line breaks
354: * after every 75 Base64 characters.
355: *
356: * @param s the string to encode
357: * @return the encoded string
358: * @since 1.3
359: */
360: public static String encodeString(String s) {
361: logger.debug("encodeString(s=" + s + ") - start");
362:
363: return encodeBytes(s.getBytes());
364: } // end encodeString
365:
366: /* ******** D E C O D I N G M E T H O D S ******** */
367:
368: /**
369: * Decodes the first four bytes of array <var>fourBytes</var>
370: * and returns an array up to three bytes long with the
371: * decoded values.
372: *
373: * @param fourBytes the array with Base64 content
374: * @return array with decoded values
375: * @since 1.3
376: */
377: private static byte[] decode4to3(byte[] fourBytes) {
378: logger.debug("decode4to3(fourBytes=" + fourBytes + ") - start");
379:
380: byte[] outBuff1 = new byte[3];
381: int count = decode4to3(fourBytes, 0, outBuff1, 0);
382: byte[] outBuff2 = new byte[count];
383:
384: for (int i = 0; i < count; i++)
385: outBuff2[i] = outBuff1[i];
386:
387: return outBuff2;
388: }
389:
390: /**
391: * Decodes four bytes from array <var>source</var>
392: * and writes the resulting bytes (up to three of them)
393: * to <var>destination</var>.
394: * The source and destination arrays can be manipulated
395: * anywhere along their length by specifying
396: * <var>srcOffset</var> and <var>destOffset</var>.
397: * This method does not check to make sure your arrays
398: * are large enough to accomodate <var>srcOffset</var> + 4 for
399: * the <var>source</var> array or <var>destOffset</var> + 3 for
400: * the <var>destination</var> array.
401: * This method returns the actual number of bytes that
402: * were converted from the Base64 encoding.
403: *
404: *
405: * @param source the array to convert
406: * @param srcOffset the index where conversion begins
407: * @param destination the array to hold the conversion
408: * @param destOffset the index where output will be put
409: * @return the number of decoded bytes converted
410: * @since 1.3
411: */
412: private static int decode4to3(byte[] source, int srcOffset,
413: byte[] destination, int destOffset) {
414: logger.debug("decode4to3(source=" + source + ", srcOffset="
415: + srcOffset + ", destination=" + destination
416: + ", destOffset=" + destOffset + ") - start");
417:
418: // Example: Dk==
419: if (source[srcOffset + 2] == EQUALS_SIGN) {
420: int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6)
421: | ((DECODABET[source[srcOffset + 1]] << 24) >>> 12);
422:
423: destination[destOffset] = (byte) (outBuff >>> 16);
424: return 1;
425: }
426:
427: // Example: DkL=
428: else if (source[srcOffset + 3] == EQUALS_SIGN) {
429: int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6)
430: | ((DECODABET[source[srcOffset + 1]] << 24) >>> 12)
431: | ((DECODABET[source[srcOffset + 2]] << 24) >>> 18);
432:
433: destination[destOffset] = (byte) (outBuff >>> 16);
434: destination[destOffset + 1] = (byte) (outBuff >>> 8);
435: return 2;
436: }
437:
438: // Example: DkLE
439: else {
440: int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6)
441: | ((DECODABET[source[srcOffset + 1]] << 24) >>> 12)
442: | ((DECODABET[source[srcOffset + 2]] << 24) >>> 18)
443: | ((DECODABET[source[srcOffset + 3]] << 24) >>> 24);
444:
445: destination[destOffset] = (byte) (outBuff >> 16);
446: destination[destOffset + 1] = (byte) (outBuff >> 8);
447: destination[destOffset + 2] = (byte) (outBuff);
448: return 3;
449: }
450: } // end decodeToBytes
451:
452: /**
453: * Decodes data from Base64 notation.
454: *
455: * @param s the string to decode
456: * @return the decoded data
457: * @since 1.4
458: */
459: public static byte[] decode(String s) {
460: logger.debug("decode(s=" + s + ") - start");
461:
462: byte[] bytes = s.getBytes();
463: return decode(bytes, 0, bytes.length);
464: } // end decode
465:
466: /**
467: * Decodes data from Base64 notation and
468: * returns it as a string.
469: * Equivlaent to calling
470: * <code>new String( decode( s ) )</code>
471: *
472: * @param s the strind to decode
473: * @return The data as a string
474: * @since 1.4
475: */
476: public static String decodeToString(String s) {
477: logger.debug("decodeToString(s=" + s + ") - start");
478:
479: return new String(decode(s));
480: } // end decodeToString
481:
482: /**
483: * Attempts to decode Base64 data and deserialize a Java
484: * Object within. Returns <tt>null if there was an error.
485: *
486: * @param encodedObject The Base64 data to decode
487: * @return The decoded and deserialized object
488: * @since 1.4
489: */
490: public static Object decodeToObject(String encodedObject) {
491: logger.debug("decodeToObject(encodedObject=" + encodedObject
492: + ") - start");
493:
494: byte[] objBytes = decode(encodedObject);
495:
496: java.io.ByteArrayInputStream bais = null;
497: java.io.ObjectInputStream ois = null;
498:
499: try {
500: bais = new java.io.ByteArrayInputStream(objBytes);
501: ois = new java.io.ObjectInputStream(bais);
502:
503: return ois.readObject();
504: } // end try
505: catch (java.io.IOException e) {
506: logger.error("decodeToObject()", e);
507:
508: e.printStackTrace();
509: return null;
510: } // end catch
511: catch (ClassNotFoundException e) {
512: logger.error("decodeToObject()", e);
513:
514: e.printStackTrace();
515: return null;
516: } // end catch
517: finally {
518: try {
519: bais.close();
520: } catch (Exception e) {
521: logger.error("decodeToObject()", e);
522: }
523: try {
524: ois.close();
525: } catch (Exception e) {
526: logger.error("decodeToObject()", e);
527: }
528: } // end finally
529: } // end decodeObject
530:
531: /**
532: * Decodes Base64 content in byte array format and returns
533: * the decoded byte array.
534: *
535: * @param source The Base64 encoded data
536: * @param off The offset of where to begin decoding
537: * @param len The length of characters to decode
538: * @return decoded data
539: * @since 1.3
540: */
541: public static byte[] decode(byte[] source, int off, int len) {
542: logger.debug("decode(source=" + source + ", off=" + off
543: + ", len=" + len + ") - start");
544:
545: int len34 = len * 3 / 4;
546: byte[] outBuff = new byte[len34]; // Upper limit on size of output
547: int outBuffPosn = 0;
548:
549: byte[] b4 = new byte[4];
550: int b4Posn = 0;
551: int i = 0;
552: byte sbiCrop = 0;
553: byte sbiDecode = 0;
554: for (i = 0; i < len; i++) {
555: sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
556: sbiDecode = DECODABET[sbiCrop];
557:
558: if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better
559: {
560: if (sbiDecode >= EQUALS_SIGN_ENC) {
561: b4[b4Posn++] = sbiCrop;
562: if (b4Posn > 3) {
563: outBuffPosn += decode4to3(b4, 0, outBuff,
564: outBuffPosn);
565: b4Posn = 0;
566:
567: // If that was the equals sign, break out of 'for' loop
568: if (sbiCrop == EQUALS_SIGN)
569: break;
570: } // end if: quartet built
571:
572: } // end if: equals sign or better
573:
574: } // end if: white space, equals sign or better
575: else {
576: System.err.println("Bad Base64 input character at " + i
577: + ": " + source[i] + "(decimal)");
578: return null;
579: } // end else:
580: } // each input character
581:
582: byte[] out = new byte[outBuffPosn];
583: System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
584: return out;
585: } // end decode
586:
587: /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
588:
589: /**
590: * A {@link Base64#InputStream} will read data from another
591: * {@link java.io.InputStream}, given in the constructor,
592: * and encode/decode to/from Base64 notation on the fly.
593: *
594: * @see Base64
595: * @see java.io.FilterInputStream
596: * @since 1.3
597: */
598: public static class InputStream extends java.io.FilterInputStream {
599:
600: /**
601: * Logger for this class
602: */
603: private static final Logger logger = LoggerFactory
604: .getLogger(InputStream.class);
605:
606: private boolean encode; // Encoding or decoding
607: private int position; // Current position in the buffer
608: private byte[] buffer; // Small buffer holding converted data
609: private int bufferLength; // Length of buffer (3 or 4)
610: private int numSigBytes; // Number of meaningful bytes in the buffer
611:
612: /**
613: * Constructs a {@link Base64#InputStream} in DECODE mode.
614: *
615: * @param in the {@link java.io.InputStream} from which to read data.
616: * @since 1.3
617: */
618: public InputStream(java.io.InputStream in) {
619: this (in, Base64.DECODE);
620: } // end constructor
621:
622: /**
623: * Constructs a {@link Base64#InputStream} in
624: * either ENCODE or DECODE mode.
625: *
626: * @param in the {@link java.io.InputStream} from which to read data.
627: * @param encode Conversion direction
628: * @see Base64#ENCODE
629: * @see Base64#DECODE
630: * @since 1.3
631: */
632: public InputStream(java.io.InputStream in, boolean encode) {
633: super (in);
634: this .encode = encode;
635: this .bufferLength = encode ? 4 : 3;
636: this .buffer = new byte[bufferLength];
637: this .position = -1;
638: } // end constructor
639:
640: /**
641: * Reads enough of the input stream to convert
642: * to/from Base64 and returns the next byte.
643: *
644: * @return next byte
645: * @since 1.3
646: */
647: public int read() throws java.io.IOException {
648: logger.debug("read() - start");
649:
650: // Do we need to get data?
651: if (position < 0) {
652: if (encode) {
653: byte[] b3 = new byte[3];
654: numSigBytes = 0;
655: for (int i = 0; i < 3; i++) {
656: try {
657: int b = in.read();
658:
659: // If end of stream, b is -1.
660: if (b >= 0) {
661: b3[i] = (byte) b;
662: numSigBytes++;
663: } // end if: not end of stream
664:
665: } // end try: read
666: catch (java.io.IOException e) {
667: logger.error("read()", e);
668:
669: // Only a problem if we got no data at all.
670: if (i == 0)
671: throw e;
672:
673: } // end catch
674: } // end for: each needed input byte
675:
676: if (numSigBytes > 0) {
677: encode3to4(b3, 0, numSigBytes, buffer, 0);
678: position = 0;
679: } // end if: got data
680: } // end if: encoding
681:
682: // Else decoding
683: else {
684: byte[] b4 = new byte[4];
685: int i = 0;
686: for (i = 0; i < 4; i++) {
687: int b = 0;
688: do {
689: b = in.read();
690: } while (b >= 0
691: && DECODABET[b & 0x7f] < WHITE_SPACE_ENC);
692:
693: if (b < 0)
694: break; // Reads a -1 if end of stream
695:
696: b4[i] = (byte) b;
697: } // end for: each needed input byte
698:
699: if (i == 4) {
700: numSigBytes = decode4to3(b4, 0, buffer, 0);
701: position = 0;
702: } // end if: got four characters
703:
704: } // end else: decode
705: } // end else: get data
706:
707: // Got data?
708: if (position >= 0) {
709: // End of relevant data?
710: if (position >= numSigBytes)
711: return -1;
712:
713: int b = buffer[position++];
714:
715: if (position >= bufferLength)
716: position = -1;
717:
718: return b;
719: } // end if: position >= 0
720:
721: // Else error
722: else
723: return -1;
724: } // end read
725:
726: /**
727: * Calls {@link #read} repeatedly until the end of stream
728: * is reached or <var>len</var> bytes are read.
729: * Returns number of bytes read into array or -1 if
730: * end of stream is encountered.
731: *
732: * @param dest array to hold values
733: * @param off offset for array
734: * @param len max number of bytes to read into array
735: * @return bytes read into array or -1 if end of stream is encountered.
736: * @since 1.3
737: */
738: public int read(byte[] dest, int off, int len)
739: throws java.io.IOException {
740: logger.debug("read(dest=" + dest + ", off=" + off
741: + ", len=" + len + ") - start");
742:
743: int i;
744: int b;
745: for (i = 0; i < len; i++) {
746: b = read();
747:
748: if (b < 0)
749: return -1;
750:
751: dest[off + i] = (byte) b;
752: } // end for: each byte read
753: return i;
754: } // end read
755:
756: } // end inner class InputStream
757:
758: /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
759:
760: /**
761: * A {@link Base64#OutputStream} will write data to another
762: * {@link java.io.OutputStream}, given in the constructor,
763: * and encode/decode to/from Base64 notation on the fly.
764: *
765: * @see Base64
766: * @see java.io.FilterOutputStream
767: * @since 1.3
768: */
769: public static class OutputStream extends java.io.FilterOutputStream {
770:
771: /**
772: * Logger for this class
773: */
774: private static final Logger logger = LoggerFactory
775: .getLogger(OutputStream.class);
776:
777: private boolean encode;
778: private int position;
779: private byte[] buffer;
780: private int bufferLength;
781: private int lineLength;
782:
783: /**
784: * Constructs a {@link Base64#OutputStream} in ENCODE mode.
785: *
786: * @param out the {@link java.io.OutputStream} to which data will be written.
787: * @since 1.3
788: */
789: public OutputStream(java.io.OutputStream out) {
790: this (out, Base64.ENCODE);
791: } // end constructor
792:
793: /**
794: * Constructs a {@link Base64#OutputStream} in
795: * either ENCODE or DECODE mode.
796: *
797: * @param out the {@link java.io.OutputStream} to which data will be written.
798: * @param encode Conversion direction
799: * @see Base64#ENCODE
800: * @see Base64#DECODE
801: * @since 1.3
802: */
803: public OutputStream(java.io.OutputStream out, boolean encode) {
804: super (out);
805: this .encode = encode;
806: this .bufferLength = encode ? 3 : 4;
807: this .buffer = new byte[bufferLength];
808: this .position = 0;
809: this .lineLength = 0;
810: } // end constructor
811:
812: /**
813: * Writes the byte to the output stream after
814: * converting to/from Base64 notation.
815: * When encoding, bytes are buffered three
816: * at a time before the output stream actually
817: * gets a write() call.
818: * When decoding, bytes are buffered four
819: * at a time.
820: *
821: * @param theByte the byte to write
822: * @since 1.3
823: */
824: public void write(int theByte) throws java.io.IOException {
825: logger.debug("write(theByte=" + theByte + ") - start");
826:
827: buffer[position++] = (byte) theByte;
828: if (position >= bufferLength) {
829: if (encode) {
830: out.write(Base64.encode3to4(buffer, bufferLength));
831:
832: lineLength += 4;
833: if (lineLength >= MAX_LINE_LENGTH) {
834: out.write(NEW_LINE);
835: lineLength = 0;
836: } // end if: end o fline
837: } // end if: encoding
838: else
839: out.write(Base64.decode4to3(buffer));
840:
841: position = 0;
842: } // end if: convert and flush
843: } // end write
844:
845: /**
846: * Calls {@link #write} repeatedly until <var>len</var>
847: * bytes are written.
848: *
849: * @param theBytes array from which to read bytes
850: * @param off offset for array
851: * @param len max number of bytes to read into array
852: * @since 1.3
853: */
854: public void write(byte[] theBytes, int off, int len)
855: throws java.io.IOException {
856: logger.debug("write(theBytes=" + theBytes + ", off=" + off
857: + ", len=" + len + ") - start");
858:
859: for (int i = 0; i < len; i++) {
860: write(theBytes[off + i]);
861: } // end for: each byte written
862:
863: } // end write
864:
865: /**
866: * Appropriately pads Base64 notation when encoding
867: * or throws an exception if Base64 input is not
868: * properly padded when decoding.
869: *
870: * @since 1.3
871: */
872: public void flush() throws java.io.IOException {
873: logger.debug("flush() - start");
874:
875: if (position > 0) {
876: if (encode) {
877: out.write(Base64.encode3to4(buffer, position));
878: } // end if: encoding
879: else {
880: throw new java.io.IOException(
881: "Base64 input not properly padded.");
882: } // end else: decoding
883: } // end if: buffer partially full
884:
885: super .flush();
886: out.flush();
887: } // end flush
888:
889: /**
890: * Flushes and closes stream.
891: *
892: * @since 1.3
893: */
894: public void close() throws java.io.IOException {
895: logger.debug("close() - start");
896:
897: this .flush();
898:
899: super .close();
900: out.close();
901:
902: buffer = null;
903: out = null;
904: } // end close
905:
906: } // end inner class OutputStream
907:
908: } // end class Base64
|