Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/mrbook/mrpostman/another/Base64.java


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