source: main/trunk/gli/src/org/greenstone/gatherer/feedback/Base64.java@ 33724

Last change on this file since 33724 was 33724, checked in by ak19, 4 years ago
  1. A bugfix to Base64.decode(String s) to handle null strings returned from Base64.decode(byte[], ...) variant. 2. Adding a copy of the stringToHex function debugUnicodeString() to gli code.
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 47.3 KB
Line 
1package org.greenstone.gatherer.feedback;
2
3import java.io.*;
4/**
5 * Encodes and decodes to and from Base64 notation.
6 *
7 * <p>
8 * Change Log:
9 * </p>
10 * <ul>
11 * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
12 * encoded data was a single byte.</li>
13 * <li>v2.0 - I got rid of methods that used booleans to set options.
14 * Now everything is more consolidated and cleaner. The code now detects
15 * when data that's being decoded is gzip-compressed and will decompress it
16 * automatically. Generally things are cleaner. You'll probably have to
17 * change some method calls that you were making to support the new
18 * options format (<tt>int</tt>s that you "OR" together).</li>
19 * <li>v1.5.1 - Fixed bug when decompressing and decoding to a
20 * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
21 * Added the ability to "suspend" encoding in the Output Stream so
22 * you can turn on and off the encoding if you need to embed base64
23 * data in an otherwise "normal" stream (like an XML file).</li>
24 * <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
25 * This helps when using GZIP streams.
26 * Added the ability to GZip-compress objects before encoding them.</li>
27 * <li>v1.4 - Added helper methods to read/write files.</li>
28 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
29 * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
30 * where last buffer being read, if not completely full, was not returned.</li>
31 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
32 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
33 * </ul>
34 *
35 * <p>
36 * I am placing this code in the Public Domain. Do with it as you will.
37 * This software comes with no guarantees or warranties but with
38 * plenty of well-wishing instead!
39 * Please visit <a href="http://iharder.net/xmlizable">http://iharder.net/base64</a>
40 * periodically to check for updates or to contribute improvements.
41 * </p>
42 *
43 * @author Robert Harder
44 * @author [email protected]
45 * @version 2.0
46 */
47public class Base64
48{
49
50/* ******** P U B L I C F I E L D S ******** */
51
52
53 /** No options specified. Value is zero. */
54 public final static int NO_OPTIONS = 0;
55
56 /** Specify encoding. */
57 public final static int ENCODE = 1;
58
59
60 /** Specify decoding. */
61 public final static int DECODE = 0;
62
63
64 /** Specify that data should be gzip-compressed. */
65 public final static int GZIP = 2;
66
67
68 /** Don't break lines when encoding (violates strict Base64 specification) */
69 public final static int DONT_BREAK_LINES = 8;
70
71
72/* ******** P R I V A T E F I E L D S ******** */
73
74
75 /** Maximum line length (76) of Base64 output. */
76 private final static int MAX_LINE_LENGTH = 76;
77
78
79 /** The equals sign (=) as a byte. */
80 private final static byte EQUALS_SIGN = (byte)'=';
81
82
83 /** The new line character (\n) as a byte. */
84 private final static byte NEW_LINE = (byte)'\n';
85
86
87 /** The 64 valid Base64 values. */
88 private final static byte[] ALPHABET =
89 {
90 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
91 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
92 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
93 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
94 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
95 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
96 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
97 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
98 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
99 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
100 };
101
102 /**
103 * Translates a Base64 value to either its 6-bit reconstruction value
104 * or a negative number indicating some other meaning.
105 **/
106 private final static byte[] DECODABET =
107 {
108 -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
109 -5,-5, // Whitespace: Tab and Linefeed
110 -9,-9, // Decimal 11 - 12
111 -5, // Whitespace: Carriage Return
112 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
113 -9,-9,-9,-9,-9, // Decimal 27 - 31
114 -5, // Whitespace: Space
115 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
116 62, // Plus sign at decimal 43
117 -9,-9,-9, // Decimal 44 - 46
118 63, // Slash at decimal 47
119 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
120 -9,-9,-9, // Decimal 58 - 60
121 -1, // Equals sign at decimal 61
122 -9,-9,-9, // Decimal 62 - 64
123 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
124 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
125 -9,-9,-9,-9,-9,-9, // Decimal 91 - 96
126 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
127 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
128 -9,-9,-9,-9 // Decimal 123 - 126
129 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
130 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
131 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
132 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
133 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
134 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
135 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
136 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
137 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
138 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
139 };
140
141 private final static byte BAD_ENCODING = -9; // Indicates error in encoding
142 private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
143 private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
144
145
146 /** Defeats instantiation. */
147 private Base64(){}
148
149
150
151/* ******** E N C O D I N G M E T H O D S ******** */
152
153
154 /**
155 * Encodes the first three bytes of array <var>threeBytes</var>
156 * and returns a four-byte array in Base64 notation.
157 *
158 * @param threeBytes the array to convert
159 * @return four byte array in Base64 notation.
160 * @since 1.3
161 */
162 private static byte[] encode3to4( byte[] threeBytes )
163 {
164 return encode3to4( threeBytes, 3 );
165 } // end encodeToBytes
166
167
168
169 /**
170 * Encodes up to the first three bytes of array <var>threeBytes</var>
171 * and returns a four-byte array in Base64 notation.
172 * The actual number of significant bytes in your array is
173 * given by <var>numSigBytes</var>.
174 * The array <var>threeBytes</var> needs only be as big as
175 * <var>numSigBytes</var>.
176 *
177 * @param threeBytes the array to convert
178 * @param numSigBytes the number of significant bytes in your array
179 * @return four byte array in Base64 notation.
180 * @since 1.3
181 */
182 private static byte[] encode3to4( byte[] threeBytes, int numSigBytes )
183 {
184 byte[] dest = new byte[4];
185 encode3to4( threeBytes, 0, numSigBytes, dest, 0 );
186 return dest;
187 }
188
189 /**
190 * Encodes up to the first three bytes of array <var>threeBytes</var>
191 * and returns a four-byte array in Base64 notation.
192 * The actual number of significant bytes in your array is
193 * given by <var>numSigBytes</var>.
194 * The array <var>threeBytes</var> needs only be as big as
195 * <var>numSigBytes</var>.
196 * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
197 *
198 * @param b4 A reusable byte array to reduce array instantiation
199 * @param threeBytes the array to convert
200 * @param numSigBytes the number of significant bytes in your array
201 * @return four byte array in Base64 notation.
202 * @since 1.5.1
203 */
204 private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes )
205 {
206 encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
207 return b4;
208 } // end encode3to4
209
210
211 /**
212 * Encodes up to three bytes of the array <var>source</var>
213 * and writes the resulting four Base64 bytes to <var>destination</var>.
214 * The source and destination arrays can be manipulated
215 * anywhere along their length by specifying
216 * <var>srcOffset</var> and <var>destOffset</var>.
217 * This method does not check to make sure your arrays
218 * are large enough to accomodate <var>srcOffset</var> + 3 for
219 * the <var>source</var> array or <var>destOffset</var> + 4 for
220 * the <var>destination</var> array.
221 * The actual number of significant bytes in your array is
222 * given by <var>numSigBytes</var>.
223 *
224 * @param source the array to convert
225 * @param srcOffset the index where conversion begins
226 * @param numSigBytes the number of significant bytes in your array
227 * @param destination the array to hold the conversion
228 * @param destOffset the index where output will be put
229 * @return the <var>destination</var> array
230 * @since 1.3
231 */
232 private static byte[] encode3to4(
233 byte[] source, int srcOffset, int numSigBytes,
234 byte[] destination, int destOffset )
235 {
236 // 1 2 3
237 // 01234567890123456789012345678901 Bit position
238 // --------000000001111111122222222 Array position from threeBytes
239 // --------| || || || | Six bit groups to index ALPHABET
240 // >>18 >>12 >> 6 >> 0 Right shift necessary
241 // 0x3f 0x3f 0x3f Additional AND
242
243 // Create buffer with zero-padding if there are only one or two
244 // significant bytes passed in the array.
245 // We have to shift left 24 in order to flush out the 1's that appear
246 // when Java treats a value as negative that is cast from a byte to an int.
247 int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
248 | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
249 | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
250
251 switch( numSigBytes )
252 {
253 case 3:
254 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
255 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
256 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
257 destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
258 return destination;
259
260 case 2:
261 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
262 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
263 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
264 destination[ destOffset + 3 ] = EQUALS_SIGN;
265 return destination;
266
267 case 1:
268 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
269 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
270 destination[ destOffset + 2 ] = EQUALS_SIGN;
271 destination[ destOffset + 3 ] = EQUALS_SIGN;
272 return destination;
273
274 default:
275 return destination;
276 } // end switch
277 } // end encode3to4
278
279
280
281 /**
282 * Serializes an object and returns the Base64-encoded
283 * version of that serialized object. If the object
284 * cannot be serialized or there is another error,
285 * the method will return <tt>null</tt>.
286 * The object is not GZip-compressed before being encoded.
287 *
288 * @param serializableObject The object to encode
289 * @return The Base64-encoded object
290 * @since 1.4
291 */
292 public static String encodeObject( java.io.Serializable serializableObject )
293 {
294 return encodeObject( serializableObject, NO_OPTIONS );
295 } // end encodeObject
296
297
298
299 /**
300 * Serializes an object and returns the Base64-encoded
301 * version of that serialized object. If the object
302 * cannot be serialized or there is another error,
303 * the method will return <tt>null</tt>.
304 * <p>
305 * Valid options:<pre>
306 * GZIP: gzip-compresses object before encoding it.
307 * DONT_BREAK_LINES: don't break lines at 76 characters
308 * <i>Note: Technically, this makes your encoding non-compliant.</i>
309 * </pre>
310 * <p>
311 * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
312 * <p>
313 * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
314 *
315 * @param serializableObject The object to encode
316 * @param options Specified options
317 * @return The Base64-encoded object
318 * @see Base64#GZIP
319 * @see Base64#DONT_BREAK_LINES
320 * @since 2.0
321 */
322 public static String encodeObject( java.io.Serializable serializableObject, int options )
323 {
324 // Streams
325 java.io.ByteArrayOutputStream baos = null;
326 java.io.OutputStream b64os = null;
327 java.io.ObjectOutputStream oos = null;
328 java.util.zip.GZIPOutputStream gzos = null;
329
330 // Isolate options
331 int gzip = (options & GZIP);
332 int dontBreakLines = (options & DONT_BREAK_LINES);
333
334 try
335 {
336 // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
337 baos = new java.io.ByteArrayOutputStream();
338 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
339
340 // GZip?
341 if( gzip == GZIP )
342 {
343 gzos = new java.util.zip.GZIPOutputStream( b64os );
344 oos = new java.io.ObjectOutputStream( gzos );
345 } // end if: gzip
346 else
347 oos = new java.io.ObjectOutputStream( b64os );
348
349 oos.writeObject( serializableObject );
350 } // end try
351 catch( java.io.IOException e )
352 {
353 e.printStackTrace();
354 return null;
355 } // end catch
356 finally
357 {
358 try{ oos.close(); } catch( Exception e ){}
359 try{ gzos.close(); } catch( Exception e ){}
360 try{ b64os.close(); } catch( Exception e ){}
361 try{ baos.close(); } catch( Exception e ){}
362 } // end finally
363
364 return new String( baos.toByteArray() );
365 } // end encode
366
367
368
369 /**
370 * Encodes a byte array into Base64 notation.
371 * Does not GZip-compress data.
372 *
373 * @param source The data to convert
374 * @since 1.4
375 */
376 public static String encodeBytes( byte[] source )
377 {
378 return encodeBytes( source, 0, source.length, NO_OPTIONS );
379 } // end encodeBytes
380
381 /**
382 * Added this variant of the method in so that RemoteGreenstoneServer.java
383 * can process large source and target filenames, without Base64 encoding
384 * them introducing a new line.
385 * @param source The data to convert
386 * @since January
387 */
388 public static String encodeBytesInSingleLine( byte[] source)
389 {
390 return encodeBytes( source, 0, source.length, DONT_BREAK_LINES);
391 } // end encodeBytes
392
393 /**
394 * Encodes a byte array into Base64 notation.
395 * <p>
396 * Valid options:<pre>
397 * GZIP: gzip-compresses object before encoding it.
398 * DONT_BREAK_LINES: don't break lines at 76 characters
399 * <i>Note: Technically, this makes your encoding non-compliant.</i>
400 * </pre>
401 * <p>
402 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
403 * <p>
404 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
405 *
406 *
407 * @param source The data to convert
408 * @param options Specified options
409 * @see Base64#GZIP
410 * @see Base64#DONT_BREAK_LINES
411 * @since 2.0
412 */
413 public static String encodeBytes( byte[] source, int options )
414 {
415 return encodeBytes( source, 0, source.length, options );
416 } // end encodeBytes
417
418
419 /**
420 * Encodes a byte array into Base64 notation.
421 * Does not GZip-compress data.
422 *
423 * @param source The data to convert
424 * @param off Offset in array where conversion should begin
425 * @param len Length of data to convert
426 * @since 1.4
427 */
428 public static String encodeBytes( byte[] source, int off, int len )
429 {
430 return encodeBytes( source, off, len, NO_OPTIONS );
431 } // end encodeBytes
432
433
434
435 /**
436 * Encodes a byte array into Base64 notation.
437 * <p>
438 * Valid options:<pre>
439 * GZIP: gzip-compresses object before encoding it.
440 * DONT_BREAK_LINES: don't break lines at 76 characters
441 * <i>Note: Technically, this makes your encoding non-compliant.</i>
442 * </pre>
443 * <p>
444 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
445 * <p>
446 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
447 *
448 *
449 * @param source The data to convert
450 * @param off Offset in array where conversion should begin
451 * @param len Length of data to convert
452 * @param options Specified options
453 * @see Base64#GZIP
454 * @see Base64#DONT_BREAK_LINES
455 * @since 2.0
456 */
457 public static String encodeBytes( byte[] source, int off, int len, int options )
458 {
459 // Isolate options
460 int dontBreakLines = ( options & DONT_BREAK_LINES );
461 int gzip = ( options & GZIP );
462
463 // Compress?
464 if( gzip == GZIP )
465 {
466 java.io.ByteArrayOutputStream baos = null;
467 java.util.zip.GZIPOutputStream gzos = null;
468 Base64.OutputStream b64os = null;
469
470
471 try
472 {
473 // GZip -> Base64 -> ByteArray
474 baos = new java.io.ByteArrayOutputStream();
475 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
476 gzos = new java.util.zip.GZIPOutputStream( b64os );
477
478 gzos.write( source, off, len );
479 gzos.close();
480 } // end try
481 catch( java.io.IOException e )
482 {
483 e.printStackTrace();
484 return null;
485 } // end catch
486 finally
487 {
488 try{ gzos.close(); } catch( Exception e ){}
489 try{ b64os.close(); } catch( Exception e ){}
490 try{ baos.close(); } catch( Exception e ){}
491 } // end finally
492
493 return new String( baos.toByteArray() );
494 } // end if: compress
495
496 // Else, don't compress. Better not to use streams at all then.
497 else
498 {
499 // Convert option to boolean in way that code likes it.
500 boolean breakLines = dontBreakLines == 0;
501
502 int len43 = len * 4 / 3;
503 byte[] outBuff = new byte[ ( len43 ) // Main 4:3
504 + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
505 + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
506 int d = 0;
507 int e = 0;
508 int len2 = len - 2;
509 int lineLength = 0;
510 for( ; d < len2; d+=3, e+=4 )
511 {
512 encode3to4( source, d+off, 3, outBuff, e );
513
514 lineLength += 4;
515 if( breakLines && lineLength == MAX_LINE_LENGTH )
516 {
517 outBuff[e+4] = NEW_LINE;
518 e++;
519 lineLength = 0;
520 } // end if: end of line
521 } // en dfor: each piece of array
522
523 if( d < len )
524 {
525 encode3to4( source, d+off, len - d, outBuff, e );
526 e += 4;
527 } // end if: some padding needed
528
529 return new String( outBuff, 0, e );
530 } // end else: don't compress
531
532 } // end encodeBytes
533
534
535
536
537
538/* ******** D E C O D I N G M E T H O D S ******** */
539
540
541 /**
542 * Decodes the first four bytes of array <var>fourBytes</var>
543 * and returns an array up to three bytes long with the
544 * decoded values.
545 *
546 * @param fourBytes the array with Base64 content
547 * @return array with decoded values
548 * @since 1.3
549 */
550 private static byte[] decode4to3( byte[] fourBytes )
551 {
552 byte[] outBuff1 = new byte[3];
553 int count = decode4to3( fourBytes, 0, outBuff1, 0 );
554 byte[] outBuff2 = new byte[ count ];
555
556 for( int i = 0; i < count; i++ )
557 outBuff2[i] = outBuff1[i];
558
559 return outBuff2;
560 }
561
562
563
564
565 /**
566 * Decodes four bytes from array <var>source</var>
567 * and writes the resulting bytes (up to three of them)
568 * to <var>destination</var>.
569 * The source and destination arrays can be manipulated
570 * anywhere along their length by specifying
571 * <var>srcOffset</var> and <var>destOffset</var>.
572 * This method does not check to make sure your arrays
573 * are large enough to accomodate <var>srcOffset</var> + 4 for
574 * the <var>source</var> array or <var>destOffset</var> + 3 for
575 * the <var>destination</var> array.
576 * This method returns the actual number of bytes that
577 * were converted from the Base64 encoding.
578 *
579 *
580 * @param source the array to convert
581 * @param srcOffset the index where conversion begins
582 * @param destination the array to hold the conversion
583 * @param destOffset the index where output will be put
584 * @return the number of decoded bytes converted
585 * @since 1.3
586 */
587 private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
588 {
589 // Example: Dk==
590 if( source[ srcOffset + 2] == EQUALS_SIGN )
591 {
592 // Two ways to do the same thing. Don't know which way I like best.
593 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
594 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
595 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
596 | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
597
598 destination[ destOffset ] = (byte)( outBuff >>> 16 );
599 return 1;
600 }
601
602 // Example: DkL=
603 else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
604 {
605 // Two ways to do the same thing. Don't know which way I like best.
606 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
607 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
608 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
609 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
610 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
611 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
612
613 destination[ destOffset ] = (byte)( outBuff >>> 16 );
614 destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
615 return 2;
616 }
617
618 // Example: DkLE
619 else
620 {
621 try{
622 // Two ways to do the same thing. Don't know which way I like best.
623 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
624 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
625 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
626 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
627 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
628 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
629 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
630 | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
631
632
633 destination[ destOffset ] = (byte)( outBuff >> 16 );
634 destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
635 destination[ destOffset + 2 ] = (byte)( outBuff );
636
637 return 3;
638 }catch( Exception e){
639 System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) );
640 System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) );
641 System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) );
642 System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) );
643 return -1;
644 } //e nd catch
645 }
646 } // end decodeToBytes
647
648
649
650
651 /**
652 * Very low-level access to decoding ASCII characters in
653 * the form of a byte array. Does not support automatically
654 * gunzipping or any other "fancy" features.
655 *
656 * @param source The Base64 encoded data
657 * @param off The offset of where to begin decoding
658 * @param len The length of characters to decode
659 * @return decoded data
660 * @since 1.3
661 */
662 public static byte[] decode( byte[] source, int off, int len )
663 {
664 int len34 = len * 3 / 4;
665 byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
666 int outBuffPosn = 0;
667
668 byte[] b4 = new byte[4];
669 int b4Posn = 0;
670 int i = 0;
671 byte sbiCrop = 0;
672 byte sbiDecode = 0;
673 for( i = off; i < off+len; i++ )
674 {
675 sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
676 sbiDecode = DECODABET[ sbiCrop ];
677
678 if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
679 {
680 if( sbiDecode >= EQUALS_SIGN_ENC )
681 {
682 b4[ b4Posn++ ] = sbiCrop;
683 if( b4Posn > 3 )
684 {
685 outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
686 b4Posn = 0;
687
688 // If that was the equals sign, break out of 'for' loop
689 if( sbiCrop == EQUALS_SIGN )
690 break;
691 } // end if: quartet built
692
693 } // end if: equals sign or better
694
695 } // end if: white space, equals sign or better
696 else
697 {
698 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
699 return null;
700 } // end else:
701 } // each input character
702
703 byte[] out = new byte[ outBuffPosn ];
704 System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
705 return out;
706 } // end decode
707
708
709
710
711 /**
712 * Decodes data from Base64 notation, automatically
713 * detecting gzip-compressed data and decompressing it.
714 *
715 * @param s the string to decode
716 * @return the decoded data
717 * @since 1.4
718 */
719 public static byte[] decode( String s )
720 {
721 byte[] bytes = s.getBytes();
722 byte[] origBytes = bytes;
723 bytes = decode( bytes, 0, bytes.length );
724
725 // if something went wrong base64 decoding the string, just return the original bytes
726 if(bytes == null) {
727 return origBytes;
728 }
729
730 // Check to see if it's gzip-compressed
731 // GZIP Magic Two-Byte Number: 0x8b1f (35615)
732 if( bytes.length >= 2 )
733 {
734
735 int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
736 if(
737 bytes != null && // In case decoding returned null
738 bytes.length >= 4 && // Don't want to get ArrayIndexOutOfBounds exception
739 java.util.zip.GZIPInputStream.GZIP_MAGIC == head )
740 {
741 java.io.ByteArrayInputStream bais = null;
742 java.util.zip.GZIPInputStream gzis = null;
743 java.io.ByteArrayOutputStream baos = null;
744 byte[] buffer = new byte[2048];
745 int length = 0;
746
747 try
748 {
749 baos = new java.io.ByteArrayOutputStream();
750 bais = new java.io.ByteArrayInputStream( bytes );
751 gzis = new java.util.zip.GZIPInputStream( bais );
752
753 while( ( length = gzis.read( buffer ) ) >= 0 )
754 {
755 baos.write(buffer,0,length);
756 } // end while: reading input
757
758 // No error? Get new bytes.
759 bytes = baos.toByteArray();
760
761 } // end try
762 catch( java.io.IOException e )
763 {
764 // Just return originally-decoded bytes
765 } // end catch
766 finally
767 {
768 try{ baos.close(); } catch( Exception e ){}
769 try{ gzis.close(); } catch( Exception e ){}
770 try{ bais.close(); } catch( Exception e ){}
771 } // end finally
772
773 } // end if: gzipped
774 } // end if: bytes.length >= 2
775
776 return bytes;
777 } // end decode
778
779
780
781
782 /**
783 * Attempts to decode Base64 data and deserialize a Java
784 * Object within. Returns <tt>null</tt> if there was an error.
785 *
786 * @param encodedObject The Base64 data to decode
787 * @return The decoded and deserialized object
788 * @since 1.5
789 */
790 public static Object decodeToObject( String encodedObject )
791 {
792 // Decode and gunzip if necessary
793 byte[] objBytes = decode( encodedObject );
794
795 java.io.ByteArrayInputStream bais = null;
796 java.io.ObjectInputStream ois = null;
797 Object obj = null;
798
799 try
800 {
801 bais = new java.io.ByteArrayInputStream( objBytes );
802 ois = new java.io.ObjectInputStream( bais );
803
804 obj = ois.readObject();
805 } // end try
806 catch( java.io.IOException e )
807 {
808 e.printStackTrace();
809 obj = null;
810 } // end catch
811 catch( java.lang.ClassNotFoundException e )
812 {
813 e.printStackTrace();
814 obj = null;
815 } // end catch
816 finally
817 {
818 try{ bais.close(); } catch( Exception e ){}
819 try{ ois.close(); } catch( Exception e ){}
820 } // end finally
821
822 return obj;
823 } // end decodeObject
824
825
826 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
827
828
829
830 /**
831 * It will read data from another
832 * {@link java.io.InputStream}, given in the constructor,
833 * and encode/decode to/from Base64 notation on the fly.
834 *
835 * @see Base64
836 * @see java.io.FilterInputStream
837 * @since 1.3
838 */
839 public static class InputStream extends java.io.FilterInputStream
840 {
841 private int options; // Options specified
842 private boolean encode; // Encoding or decoding
843 private int position; // Current position in the buffer
844 private byte[] buffer; // Small buffer holding converted data
845 private int bufferLength; // Length of buffer (3 or 4)
846 private int numSigBytes; // Number of meaningful bytes in the buffer
847 private int lineLength;
848 private boolean breakLines; // Break lines at less than 80 characters
849
850
851 /**
852 * Constructs a InputStream in DECODE mode.
853 *
854 * @param in the {@link java.io.InputStream} from which to read data.
855 * @since 1.3
856 */
857 public InputStream( java.io.InputStream in )
858 {
859 this( in, DECODE );
860 } // end constructor
861
862
863 /**
864 * Constructs a InputStream in
865 * either ENCODE or DECODE mode.
866 * <p>
867 * Valid options:<pre>
868 * ENCODE or DECODE: Encode or Decode as data is read.
869 * DONT_BREAK_LINES: don't break lines at 76 characters
870 * (only meaningful when encoding)
871 * <i>Note: Technically, this makes your encoding non-compliant.</i>
872 * </pre>
873 * <p>
874 * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
875 *
876 *
877 * @param in the {@link java.io.InputStream} from which to read data.
878 * @param options Specified options
879 * @see Base64#ENCODE
880 * @see Base64#DECODE
881 * @see Base64#DONT_BREAK_LINES
882 * @since 2.0
883 */
884 public InputStream( java.io.InputStream in, int options )
885 {
886 super( in );
887 this.options = options;
888 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
889 this.encode = (options & ENCODE) == ENCODE;
890 this.bufferLength = encode ? 4 : 3;
891 this.buffer = new byte[ bufferLength ];
892 this.position = -1;
893 this.lineLength = 0;
894 } // end constructor
895
896 /**
897 * Reads enough of the input stream to convert
898 * to/from Base64 and returns the next byte.
899 *
900 * @return next byte
901 * @since 1.3
902 */
903 public int read() throws java.io.IOException
904 {
905 // Do we need to get data?
906 if( position < 0 )
907 {
908 if( encode )
909 {
910 byte[] b3 = new byte[3];
911 int numBinaryBytes = 0;
912 for( int i = 0; i < 3; i++ )
913 {
914 try
915 {
916 int b = in.read();
917
918 // If end of stream, b is -1.
919 if( b >= 0 )
920 {
921 b3[i] = (byte)b;
922 numBinaryBytes++;
923 } // end if: not end of stream
924
925 } // end try: read
926 catch( java.io.IOException e )
927 {
928 // Only a problem if we got no data at all.
929 if( i == 0 )
930 throw e;
931
932 } // end catch
933 } // end for: each needed input byte
934
935 if( numBinaryBytes > 0 )
936 {
937 encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
938 position = 0;
939 numSigBytes = 4;
940 } // end if: got data
941 else
942 {
943 return -1;
944 } // end else
945 } // end if: encoding
946
947 // Else decoding
948 else
949 {
950 byte[] b4 = new byte[4];
951 int i = 0;
952 for( i = 0; i < 4; i++ )
953 {
954 // Read four "meaningful" bytes:
955 int b = 0;
956 do{ b = in.read(); }
957 while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
958
959 if( b < 0 )
960 break; // Reads a -1 if end of stream
961
962 b4[i] = (byte)b;
963 } // end for: each needed input byte
964
965 if( i == 4 )
966 {
967 numSigBytes = decode4to3( b4, 0, buffer, 0 );
968 position = 0;
969 } // end if: got four characters
970 else if( i == 0 ){
971 return -1;
972 } // end else if: also padded correctly
973 else
974 {
975 // Must have broken out from above.
976 throw new java.io.IOException( "Improperly padded Base64 input." );
977 } // end
978
979 } // end else: decode
980 } // end else: get data
981
982 // Got data?
983 if( position >= 0 )
984 {
985 // End of relevant data?
986 if( /*!encode &&*/ position >= numSigBytes )
987 return -1;
988
989 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
990 {
991 lineLength = 0;
992 return '\n';
993 } // end if
994 else
995 {
996 lineLength++; // This isn't important when decoding
997 // but throwing an extra "if" seems
998 // just as wasteful.
999
1000 int b = buffer[ position++ ];
1001
1002 if( position >= bufferLength )
1003 position = -1;
1004
1005 return b & 0xFF; // This is how you "cast" a byte that's
1006 // intended to be unsigned.
1007 } // end else
1008 } // end if: position >= 0
1009
1010 // Else error
1011 else
1012 {
1013 // When JDK1.4 is more accepted, use an assertion here.
1014 throw new java.io.IOException( "Error in Base64 code reading stream." );
1015 } // end else
1016 } // end read
1017
1018
1019 /**
1020 * Calls {@link #read} repeatedly until the end of stream
1021 * is reached or <var>len</var> bytes are read.
1022 * Returns number of bytes read into array or -1 if
1023 * end of stream is encountered.
1024 *
1025 * @param dest array to hold values
1026 * @param off offset for array
1027 * @param len max number of bytes to read into array
1028 * @return bytes read into array or -1 if end of stream is encountered.
1029 * @since 1.3
1030 */
1031 public int read( byte[] dest, int off, int len ) throws java.io.IOException
1032 {
1033 int i;
1034 int b;
1035 for( i = 0; i < len; i++ )
1036 {
1037 b = read();
1038
1039 //if( b < 0 && i == 0 )
1040 // return -1;
1041
1042 if( b >= 0 )
1043 dest[off + i] = (byte)b;
1044 else if( i == 0 )
1045 return -1;
1046 else
1047 break; // Out of 'for' loop
1048 } // end for: each byte read
1049 return i;
1050 } // end read
1051
1052 } // end inner class InputStream
1053
1054
1055
1056
1057
1058
1059 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1060
1061
1062
1063 /**
1064 * A OutputStream will write data to another
1065 * {@link java.io.OutputStream}, given in the constructor,
1066 * and encode/decode to/from Base64 notation on the fly.
1067 *
1068 * @see Base64
1069 * @see java.io.FilterOutputStream
1070 * @since 1.3
1071 */
1072 public static class OutputStream extends java.io.FilterOutputStream
1073 {
1074 private int options;
1075 private boolean encode;
1076 private int position;
1077 private byte[] buffer;
1078 private int bufferLength;
1079 private int lineLength;
1080 private boolean breakLines;
1081 private byte[] b4; // Scratch used in a few places
1082 private boolean suspendEncoding;
1083
1084 /**
1085 * Constructs a OutputStream in ENCODE mode.
1086 *
1087 * @param out the {@link java.io.OutputStream} to which data will be written.
1088 * @since 1.3
1089 */
1090 public OutputStream( java.io.OutputStream out )
1091 {
1092 this( out, ENCODE );
1093 } // end constructor
1094
1095
1096 /**
1097 * Constructs a OutputStream in
1098 * either ENCODE or DECODE mode.
1099 * <p>
1100 * Valid options:<pre>
1101 * ENCODE or DECODE: Encode or Decode as data is read.
1102 * DONT_BREAK_LINES: don't break lines at 76 characters
1103 * (only meaningful when encoding)
1104 * <i>Note: Technically, this makes your encoding non-compliant.</i>
1105 * </pre>
1106 * <p>
1107 * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1108 *
1109 * @param out the {@link java.io.OutputStream} to which data will be written.
1110 * @param options Specified options.
1111 * @see Base64#ENCODE
1112 * @see Base64#DECODE
1113 * @see Base64#DONT_BREAK_LINES
1114 * @since 1.3
1115 */
1116 public OutputStream( java.io.OutputStream out, int options )
1117 {
1118 super( out );
1119 this.options = options;
1120 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1121 this.encode = (options & ENCODE) == ENCODE;
1122 this.bufferLength = encode ? 3 : 4;
1123 this.buffer = new byte[ bufferLength ];
1124 this.position = 0;
1125 this.lineLength = 0;
1126 this.suspendEncoding = false;
1127 this.b4 = new byte[4];
1128 } // end constructor
1129
1130
1131 /**
1132 * Writes the byte to the output stream after
1133 * converting to/from Base64 notation.
1134 * When encoding, bytes are buffered three
1135 * at a time before the output stream actually
1136 * gets a write() call.
1137 * When decoding, bytes are buffered four
1138 * at a time.
1139 *
1140 * @param theByte the byte to write
1141 * @since 1.3
1142 */
1143 public void write(int theByte) throws java.io.IOException
1144 {
1145 // Encoding suspended?
1146 if( suspendEncoding )
1147 {
1148 super.out.write( theByte );
1149 return;
1150 } // end if: supsended
1151
1152 // Encode?
1153 if( encode )
1154 {
1155 buffer[ position++ ] = (byte)theByte;
1156 if( position >= bufferLength ) // Enough to encode.
1157 {
1158 out.write( encode3to4( b4, buffer, bufferLength ) );
1159
1160 lineLength += 4;
1161 if( breakLines && lineLength >= MAX_LINE_LENGTH )
1162 {
1163 out.write( NEW_LINE );
1164 lineLength = 0;
1165 } // end if: end of line
1166
1167 position = 0;
1168 } // end if: enough to output
1169 } // end if: encoding
1170
1171 // Else, Decoding
1172 else
1173 {
1174 // Meaningful Base64 character?
1175 if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1176 {
1177 buffer[ position++ ] = (byte)theByte;
1178 if( position >= bufferLength ) // Enough to output.
1179 {
1180 int len = Base64.decode4to3( buffer, 0, b4, 0 );
1181 out.write( b4, 0, len );
1182 //out.write( Base64.decode4to3( buffer ) );
1183 position = 0;
1184 } // end if: enough to output
1185 } // end if: meaningful base64 character
1186 else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1187 {
1188 throw new java.io.IOException( "Invalid character in Base64 data." );
1189 } // end else: not white space either
1190 } // end else: decoding
1191 } // end write
1192
1193
1194
1195 /**
1196 * Calls {@link #write} repeatedly until <var>len</var>
1197 * bytes are written.
1198 *
1199 * @param theBytes array from which to read bytes
1200 * @param off offset for array
1201 * @param len max number of bytes to read into array
1202 * @since 1.3
1203 */
1204 public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
1205 {
1206 // Encoding suspended?
1207 if( suspendEncoding )
1208 {
1209 super.out.write( theBytes, off, len );
1210 return;
1211 } // end if: supsended
1212
1213 for( int i = 0; i < len; i++ )
1214 {
1215 write( theBytes[ off + i ] );
1216 } // end for: each byte written
1217
1218 } // end write
1219
1220
1221
1222 /**
1223 * Method added by PHIL. [Thanks, PHIL. -Rob]
1224 * This pads the buffer without closing the stream.
1225 */
1226 public void flushBase64() throws java.io.IOException
1227 {
1228 if( position > 0 )
1229 {
1230 if( encode )
1231 {
1232 out.write( encode3to4( b4, buffer, position ) );
1233 position = 0;
1234 } // end if: encoding
1235 else
1236 {
1237 throw new java.io.IOException( "Base64 input not properly padded." );
1238 } // end else: decoding
1239 } // end if: buffer partially full
1240
1241 } // end flush
1242
1243
1244 /**
1245 * Flushes and closes (I think, in the superclass) the stream.
1246 *
1247 * @since 1.3
1248 */
1249 public void close() throws java.io.IOException
1250 {
1251 // 1. Ensure that pending characters are written
1252 flushBase64();
1253
1254 // 2. Actually close the stream
1255 // Base class both flushes and closes.
1256 super.close();
1257
1258 buffer = null;
1259 out = null;
1260 } // end close
1261
1262
1263
1264 /**
1265 * Suspends encoding of the stream.
1266 * May be helpful if you need to embed a piece of
1267 * base640-encoded data in a stream.
1268 *
1269 * @since 1.5.1
1270 */
1271 public void suspendEncoding() throws java.io.IOException
1272 {
1273 flushBase64();
1274 this.suspendEncoding = true;
1275 } // end suspendEncoding
1276
1277
1278 /**
1279 * Resumes encoding of the stream.
1280 * May be helpful if you need to embed a piece of
1281 * base640-encoded data in a stream.
1282 *
1283 * @since 1.5.1
1284 */
1285 public void resumeEncoding()
1286 {
1287 this.suspendEncoding = false;
1288 } // end resumeEncoding
1289
1290
1291
1292 } // end inner class OutputStream
1293
1294
1295} // end class Base64
Note: See TracBrowser for help on using the repository browser.