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