This project has retired. For details please refer to its Attic page.
Base64 xref

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.commons.impl;
20  
21  /**
22   * <p>
23   * Encodes and decodes to and from Base64 notation.
24   * </p>
25   * <p>
26   * Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.
27   * </p>
28   * 
29   * @author Robert Harder
30   * @author rob@iharder.net
31   * @version 2.3.7
32   */
33  public class Base64 {
34  
35      /* ******** P U B L I C F I E L D S ******** */
36  
37      /** No options specified. Value is zero. */
38      public static final int NO_OPTIONS = 0;
39  
40      /** Specify encoding in first bit. Value is one. */
41      public static final int ENCODE = 1;
42  
43      /** Specify decoding in first bit. Value is zero. */
44      public static final int DECODE = 0;
45  
46      /** Specify that data should be gzip-compressed in second bit. Value is two. */
47      public static final int GZIP = 2;
48  
49      /**
50       * Specify that gzipped data should <em>not</em> be automatically gunzipped.
51       */
52      public static final int DONT_GUNZIP = 4;
53  
54      /** Do break lines when encoding. Value is 8. */
55      public static final int DO_BREAK_LINES = 8;
56  
57      /**
58       * Encode using Base64-like encoding that is URL- and Filename-safe as
59       * described in Section 4 of RFC3548: <a
60       * href="http://www.faqs.org/rfcs/rfc3548.html"
61       * >http://www.faqs.org/rfcs/rfc3548.html</a>. It is important to note that
62       * data encoded this way is <em>not</em> officially valid Base64, or at the
63       * very least should not be called Base64 without also specifying that is
64       * was encoded using the URL- and Filename-safe dialect.
65       */
66      public static final int URL_SAFE = 16;
67  
68      /**
69       * Encode using the special "ordered" dialect of Base64 described here: <a
70       * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-
71       * 1940.html</a>.
72       */
73      public static final int ORDERED = 32;
74  
75      /* ******** P R I V A T E F I E L D S ******** */
76  
77      /** Maximum line length (76) of Base64 output. */
78      private static final int MAX_LINE_LENGTH = 76;
79  
80      /** The equals sign (=) as a byte. */
81      private static final byte EQUALS_SIGN = (byte) '=';
82  
83      /** The new line character (\n) as a byte. */
84      private static final byte NEW_LINE = (byte) '\n';
85  
86      /** Preferred encoding. */
87      private static final String PREFERRED_ENCODING = "US-ASCII";
88  
89      private static final byte WHITE_SPACE_ENC = -5; // Indicates white space in
90                                                      // encoding
91                                                      private static final byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in
92                                                      // encoding
93  
94      /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
95  
96      /** The 64 valid Base64 values. */
97      /*
98       * Host platform me be something funny like EBCDIC, so we hardcode these
99       * values.
100      */
101     private static final byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E',
102             (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
103             (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W',
104             (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
105             (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
106             (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
107             (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
108             (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' };
109 
110     /**
111      * Translates a Base64 value to either its 6-bit reconstruction value or a
112      * negative number indicating some other meaning.
113      **/
114     private static final byte[] _STANDARD_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal
115                                                                                             // 0
116                                                                                             // -
117                                                                                             // 8
118             -5, -5, // Whitespace: Tab and Linefeed
119             -9, -9, // Decimal 11 - 12
120             -5, // Whitespace: Carriage Return
121             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
122                                                                 // 26
123             -9, -9, -9, -9, -9, // Decimal 27 - 31
124             -5, // Whitespace: Space
125             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
126             62, // Plus sign at decimal 43
127             -9, -9, -9, // Decimal 44 - 46
128             63, // Slash at decimal 47
129             52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
130             -9, -9, -9, // Decimal 58 - 60
131             -1, // Equals sign at decimal 61
132             -9, -9, -9, // Decimal 62 - 64
133             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through
134                                                           // 'N'
135             14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
136                                                             // through 'Z'
137             -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
138             26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
139                                                                 // through 'm'
140             39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
141                                                                 // through 'z'
142             -9, -9, -9, -9, -9 // Decimal 123 - 127
143             , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 -
144                                                               // 139
145             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 -
146                                                                 // 152
147             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 -
148                                                                 // 165
149             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 -
150                                                                 // 178
151             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 -
152                                                                 // 191
153             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 -
154                                                                 // 204
155             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 -
156                                                                 // 217
157             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 -
158                                                                 // 230
159             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 -
160                                                                 // 243
161             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
162     };
163 
164     /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
165 
166     /**
167      * Used in the URL- and Filename-safe dialect described in Section 4 of
168      * RFC3548: <a
169      * href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org
170      * /rfcs/rfc3548.html</a>. Notice that the last two bytes become "hyphen"
171      * and "underscore" instead of "plus" and "slash."
172      */
173     private static final byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E',
174             (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
175             (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W',
176             (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
177             (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
178             (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
179             (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
180             (byte) '7', (byte) '8', (byte) '9', (byte) '-', (byte) '_' };
181 
182     /**
183      * Used in decoding URL- and Filename-safe dialects of Base64.
184      */
185     private static final byte[] _URL_SAFE_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal
186                                                                                             // 0
187                                                                                             // -
188                                                                                             // 8
189             -5, -5, // Whitespace: Tab and Linefeed
190             -9, -9, // Decimal 11 - 12
191             -5, // Whitespace: Carriage Return
192             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
193                                                                 // 26
194             -9, -9, -9, -9, -9, // Decimal 27 - 31
195             -5, // Whitespace: Space
196             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
197             -9, // Plus sign at decimal 43
198             -9, // Decimal 44
199             62, // Minus sign at decimal 45
200             -9, // Decimal 46
201             -9, // Slash at decimal 47
202             52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
203             -9, -9, -9, // Decimal 58 - 60
204             -1, // Equals sign at decimal 61
205             -9, -9, -9, // Decimal 62 - 64
206             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through
207                                                           // 'N'
208             14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
209                                                             // through 'Z'
210             -9, -9, -9, -9, // Decimal 91 - 94
211             63, // Underscore at decimal 95
212             -9, // Decimal 96
213             26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
214                                                                 // through 'm'
215             39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
216                                                                 // through 'z'
217             -9, -9, -9, -9, -9 // Decimal 123 - 127
218             , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 -
219                                                               // 139
220             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 -
221                                                                 // 152
222             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 -
223                                                                 // 165
224             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 -
225                                                                 // 178
226             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 -
227                                                                 // 191
228             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 -
229                                                                 // 204
230             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 -
231                                                                 // 217
232             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 -
233                                                                 // 230
234             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 -
235                                                                 // 243
236             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
237     };
238 
239     /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
240 
241     /**
242      * I don't get the point of this technique, but someone requested it, and it
243      * is described here: <a
244      * href="http://www.faqs.org/qa/rfcc-1940.html">http://
245      * www.faqs.org/qa/rfcc-1940.html</a>.
246      */
247     private static final byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
248             (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C',
249             (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
250             (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
251             (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c',
252             (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
253             (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
254             (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z' };
255 
256     /**
257      * Used in decoding the "ordered" dialect of Base64.
258      */
259     private static final byte[] _ORDERED_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal
260                                                                                            // 0
261                                                                                            // -
262                                                                                            // 8
263             -5, -5, // Whitespace: Tab and Linefeed
264             -9, -9, // Decimal 11 - 12
265             -5, // Whitespace: Carriage Return
266             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
267                                                                 // 26
268             -9, -9, -9, -9, -9, // Decimal 27 - 31
269             -5, // Whitespace: Space
270             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
271             -9, // Plus sign at decimal 43
272             -9, // Decimal 44
273             0, // Minus sign at decimal 45
274             -9, // Decimal 46
275             -9, // Slash at decimal 47
276             1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine
277             -9, -9, -9, // Decimal 58 - 60
278             -1, // Equals sign at decimal 61
279             -9, -9, -9, // Decimal 62 - 64
280             11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A'
281                                                                 // through 'M'
282             24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N'
283                                                                 // through 'Z'
284             -9, -9, -9, -9, // Decimal 91 - 94
285             37, // Underscore at decimal 95
286             -9, // Decimal 96
287             38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a'
288                                                                 // through 'm'
289             51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n'
290                                                                 // through 'z'
291             -9, -9, -9, -9, -9 // Decimal 123 - 127
292             , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128
293                                                                   // - 139
294             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 -
295                                                                 // 152
296             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 -
297                                                                 // 165
298             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 -
299                                                                 // 178
300             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 -
301                                                                 // 191
302             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 -
303                                                                 // 204
304             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 -
305                                                                 // 217
306             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 -
307                                                                 // 230
308             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 -
309                                                                 // 243
310             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
311     };
312 
313     /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */
314 
315     /**
316      * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the
317      * options specified. It's possible, though silly, to specify ORDERED
318      * <b>and</b> URLSAFE in which case one of them will be picked, though there
319      * is no guarantee as to which one will be picked.
320      */
321     private static final byte[] getAlphabet(int options) {
322         if ((options & URL_SAFE) == URL_SAFE) {
323             return _URL_SAFE_ALPHABET;
324         } else if ((options & ORDERED) == ORDERED) {
325             return _ORDERED_ALPHABET;
326         } else {
327             return _STANDARD_ALPHABET;
328         }
329     } // end getAlphabet
330 
331     /**
332      * Returns one of the _SOMETHING_DECODABET byte arrays depending on the
333      * options specified. It's possible, though silly, to specify ORDERED and
334      * URL_SAFE in which case one of them will be picked, though there is no
335      * guarantee as to which one will be picked.
336      */
337     private static final byte[] getDecodabet(int options) {
338         if ((options & URL_SAFE) == URL_SAFE) {
339             return _URL_SAFE_DECODABET;
340         } else if ((options & ORDERED) == ORDERED) {
341             return _ORDERED_DECODABET;
342         } else {
343             return _STANDARD_DECODABET;
344         }
345     } // end getAlphabet
346 
347     /** Defeats instantiation. */
348     private Base64() {
349     }
350 
351     /* ******** E N C O D I N G M E T H O D S ******** */
352 
353     /**
354      * Encodes up to the first three bytes of array <var>threeBytes</var> and
355      * returns a four-byte array in Base64 notation. The actual number of
356      * significant bytes in your array is given by <var>numSigBytes</var>. The
357      * array <var>threeBytes</var> needs only be as big as
358      * <var>numSigBytes</var>. Code can reuse a byte array by passing a
359      * four-byte array as <var>b4</var>.
360      * 
361      * @param b4
362      *            A reusable byte array to reduce array instantiation
363      * @param threeBytes
364      *            the array to convert
365      * @param numSigBytes
366      *            the number of significant bytes in your array
367      * @return four byte array in Base64 notation.
368      * @since 1.5.1
369      */
370     private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes, int options) {
371         encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
372         return b4;
373     } // end encode3to4
374 
375     /**
376      * <p>
377      * Encodes up to three bytes of the array <var>source</var> and writes the
378      * resulting four Base64 bytes to <var>destination</var>. The source and
379      * destination arrays can be manipulated anywhere along their length by
380      * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
381      * does not check to make sure your arrays are large enough to accomodate
382      * <var>srcOffset</var> + 3 for the <var>source</var> array or
383      * <var>destOffset</var> + 4 for the <var>destination</var> array. The
384      * actual number of significant bytes in your array is given by
385      * <var>numSigBytes</var>.
386      * </p>
387      * <p>
388      * This is the lowest level of the encoding methods with all possible
389      * parameters.
390      * </p>
391      * 
392      * @param source
393      *            the array to convert
394      * @param srcOffset
395      *            the index where conversion begins
396      * @param numSigBytes
397      *            the number of significant bytes in your array
398      * @param destination
399      *            the array to hold the conversion
400      * @param destOffset
401      *            the index where output will be put
402      * @return the <var>destination</var> array
403      * @since 1.3
404      */
405     private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset,
406             int options) {
407 
408         byte[] ALPHABET = getAlphabet(options);
409 
410         // 1 2 3
411         // 01234567890123456789012345678901 Bit position
412         // --------000000001111111122222222 Array position from threeBytes
413         // --------| || || || | Six bit groups to index ALPHABET
414         // >>18 >>12 >> 6 >> 0 Right shift necessary
415         // 0x3f 0x3f 0x3f Additional AND
416 
417         // Create buffer with zero-padding if there are only one or two
418         // significant bytes passed in the array.
419         // We have to shift left 24 in order to flush out the 1's that appear
420         // when Java treats a value as negative that is cast from a byte to an
421         // int.
422         int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
423                 | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
424                 | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
425 
426         switch (numSigBytes) {
427         case 3:
428             destination[destOffset] = ALPHABET[(inBuff >>> 18)];
429             destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
430             destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
431             destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
432             return destination;
433 
434         case 2:
435             destination[destOffset] = ALPHABET[(inBuff >>> 18)];
436             destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
437             destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
438             destination[destOffset + 3] = EQUALS_SIGN;
439             return destination;
440 
441         case 1:
442             destination[destOffset] = ALPHABET[(inBuff >>> 18)];
443             destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
444             destination[destOffset + 2] = EQUALS_SIGN;
445             destination[destOffset + 3] = EQUALS_SIGN;
446             return destination;
447 
448         default:
449             return destination;
450         } // end switch
451     } // end encode3to4
452 
453     /**
454      * Performs Base64 encoding on the <code>raw</code> ByteBuffer, writing it
455      * to the <code>encoded</code> ByteBuffer. This is an experimental feature.
456      * Currently it does not pass along any options (such as
457      * {@link #DO_BREAK_LINES} or {@link #GZIP}.
458      * 
459      * @param raw
460      *            input buffer
461      * @param encoded
462      *            output buffer
463      * @since 2.3
464      */
465     public static void encode(java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded) {
466         byte[] raw3 = new byte[3];
467         byte[] enc4 = new byte[4];
468 
469         while (raw.hasRemaining()) {
470             int rem = Math.min(3, raw.remaining());
471             raw.get(raw3, 0, rem);
472             Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
473             encoded.put(enc4);
474         } // end input remaining
475     }
476 
477     /**
478      * Performs Base64 encoding on the <code>raw</code> ByteBuffer, writing it
479      * to the <code>encoded</code> CharBuffer. This is an experimental feature.
480      * Currently it does not pass along any options (such as
481      * {@link #DO_BREAK_LINES} or {@link #GZIP}.
482      * 
483      * @param raw
484      *            input buffer
485      * @param encoded
486      *            output buffer
487      * @since 2.3
488      */
489     public static void encode(java.nio.ByteBuffer raw, java.nio.CharBuffer encoded) {
490         byte[] raw3 = new byte[3];
491         byte[] enc4 = new byte[4];
492 
493         while (raw.hasRemaining()) {
494             int rem = Math.min(3, raw.remaining());
495             raw.get(raw3, 0, rem);
496             Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
497             for (int i = 0; i < 4; i++) {
498                 encoded.put((char) (enc4[i] & 0xFF));
499             }
500         } // end input remaining
501     }
502 
503     /**
504      * Serializes an object and returns the Base64-encoded version of that
505      * serialized object.
506      * 
507      * <p>
508      * As of v 2.3, if the object cannot be serialized or there is another
509      * error, the method will throw an java.io.IOException. <b>This is new to
510      * v2.3!</b> In earlier versions, it just returned a null value, but in
511      * retrospect that's a pretty poor way to handle it.
512      * </p>
513      * 
514      * The object is not GZip-compressed before being encoded.
515      * 
516      * @param serializableObject
517      *            The object to encode
518      * @return The Base64-encoded object
519      * @throws java.io.IOException
520      *             if there is an error
521      * @throws NullPointerException
522      *             if serializedObject is null
523      * @since 1.4
524      */
525     public static String encodeObject(java.io.Serializable serializableObject) throws java.io.IOException {
526         return encodeObject(serializableObject, NO_OPTIONS);
527     } // end encodeObject
528 
529     /**
530      * Serializes an object and returns the Base64-encoded version of that
531      * serialized object.
532      * 
533      * <p>
534      * As of v 2.3, if the object cannot be serialized or there is another
535      * error, the method will throw an java.io.IOException. <b>This is new to
536      * v2.3!</b> In earlier versions, it just returned a null value, but in
537      * retrospect that's a pretty poor way to handle it.
538      * </p>
539      * 
540      * The object is not GZip-compressed before being encoded.
541      * <p>
542      * Example options:
543      * 
544      * <pre>
545      *   GZIP: gzip-compresses object before encoding it.
546      *   DO_BREAK_LINES: break lines at 76 characters
547      * </pre>
548      * <p>
549      * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
550      * <p>
551      * Example:
552      * <code>encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
553      * 
554      * @param serializableObject
555      *            The object to encode
556      * @param options
557      *            Specified options
558      * @return The Base64-encoded object
559      * @see Base64#GZIP
560      * @see Base64#DO_BREAK_LINES
561      * @throws java.io.IOException
562      *             if there is an error
563      * @since 2.0
564      */
565     public static String encodeObject(java.io.Serializable serializableObject, int options) throws java.io.IOException {
566 
567         if (serializableObject == null) {
568             throw new NullPointerException("Cannot serialize a null object.");
569         } // end if: null
570 
571         // Streams
572         java.io.ByteArrayOutputStream baos = null;
573         java.io.OutputStream b64os = null;
574         java.util.zip.GZIPOutputStream gzos = null;
575         java.io.ObjectOutputStream oos = null;
576 
577         try {
578             // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
579             baos = new java.io.ByteArrayOutputStream();
580             b64os = new Base64.OutputStream(baos, ENCODE | options);
581             if ((options & GZIP) != 0) {
582                 // Gzip
583                 gzos = new java.util.zip.GZIPOutputStream(b64os);
584                 oos = new java.io.ObjectOutputStream(gzos);
585             } else {
586                 // Not gzipped
587                 oos = new java.io.ObjectOutputStream(b64os);
588             }
589             oos.writeObject(serializableObject);
590         } // end try
591         catch (java.io.IOException e) {
592             // Catch it and then throw it immediately so that
593             // the finally{} block is called for cleanup.
594             throw e;
595         } // end catch
596         finally {
597             try {
598                 oos.close();
599             } catch (Exception e) {
600             }
601             try {
602                 gzos.close();
603             } catch (Exception e) {
604             }
605             try {
606                 b64os.close();
607             } catch (Exception e) {
608             }
609             try {
610                 baos.close();
611             } catch (Exception e) {
612             }
613         } // end finally
614 
615         // Return value according to relevant encoding.
616         try {
617             return new String(baos.toByteArray(), PREFERRED_ENCODING);
618         } // end try
619         catch (java.io.UnsupportedEncodingException uue) {
620             // Fall back to some Java default
621             return new String(baos.toByteArray());
622         } // end catch
623 
624     } // end encode
625 
626     /**
627      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
628      * 
629      * @param source
630      *            The data to convert
631      * @return The data in Base64-encoded form
632      * @throws NullPointerException
633      *             if source array is null
634      * @since 1.4
635      */
636     public static String encodeBytes(byte[] source) {
637         // Since we're not going to have the GZIP encoding turned on,
638         // we're not going to have an java.io.IOException thrown, so
639         // we should not force the user to have to catch it.
640         String encoded = null;
641         try {
642             encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
643         } catch (java.io.IOException ex) {
644             assert false : ex.getMessage();
645         } // end catch
646         assert encoded != null;
647         return encoded;
648     } // end encodeBytes
649 
650     /**
651      * Encodes a byte array into Base64 notation.
652      * <p>
653      * Example options:
654      * 
655      * <pre>
656      *   GZIP: gzip-compresses object before encoding it.
657      *   DO_BREAK_LINES: break lines at 76 characters
658      *     <i>Note: Technically, this makes your encoding non-compliant.</i>
659      * </pre>
660      * <p>
661      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
662      * <p>
663      * Example:
664      * <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
665      * 
666      * 
667      * <p>
668      * As of v 2.3, if there is an error with the GZIP stream, the method will
669      * throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
670      * versions, it just returned a null value, but in retrospect that's a
671      * pretty poor way to handle it.
672      * </p>
673      * 
674      * 
675      * @param source
676      *            The data to convert
677      * @param options
678      *            Specified options
679      * @return The Base64-encoded data as a String
680      * @see Base64#GZIP
681      * @see Base64#DO_BREAK_LINES
682      * @throws java.io.IOException
683      *             if there is an error
684      * @throws NullPointerException
685      *             if source array is null
686      * @since 2.0
687      */
688     public static String encodeBytes(byte[] source, int options) throws java.io.IOException {
689         return encodeBytes(source, 0, source.length, options);
690     } // end encodeBytes
691 
692     /**
693      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
694      * 
695      * <p>
696      * As of v 2.3, if there is an error, the method will throw an
697      * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
698      * just returned a null value, but in retrospect that's a pretty poor way to
699      * handle it.
700      * </p>
701      * 
702      * 
703      * @param source
704      *            The data to convert
705      * @param off
706      *            Offset in array where conversion should begin
707      * @param len
708      *            Length of data to convert
709      * @return The Base64-encoded data as a String
710      * @throws NullPointerException
711      *             if source array is null
712      * @throws IllegalArgumentException
713      *             if source array, offset, or length are invalid
714      * @since 1.4
715      */
716     public static String encodeBytes(byte[] source, int off, int len) {
717         // Since we're not going to have the GZIP encoding turned on,
718         // we're not going to have an java.io.IOException thrown, so
719         // we should not force the user to have to catch it.
720         String encoded = null;
721         try {
722             encoded = encodeBytes(source, off, len, NO_OPTIONS);
723         } catch (java.io.IOException ex) {
724             assert false : ex.getMessage();
725         } // end catch
726         assert encoded != null;
727         return encoded;
728     } // end encodeBytes
729 
730     /**
731      * Encodes a byte array into Base64 notation.
732      * <p>
733      * Example options:
734      * 
735      * <pre>
736      *   GZIP: gzip-compresses object before encoding it.
737      *   DO_BREAK_LINES: break lines at 76 characters
738      *     <i>Note: Technically, this makes your encoding non-compliant.</i>
739      * </pre>
740      * <p>
741      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
742      * <p>
743      * Example:
744      * <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
745      * 
746      * 
747      * <p>
748      * As of v 2.3, if there is an error with the GZIP stream, the method will
749      * throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
750      * versions, it just returned a null value, but in retrospect that's a
751      * pretty poor way to handle it.
752      * </p>
753      * 
754      * 
755      * @param source
756      *            The data to convert
757      * @param off
758      *            Offset in array where conversion should begin
759      * @param len
760      *            Length of data to convert
761      * @param options
762      *            Specified options
763      * @return The Base64-encoded data as a String
764      * @see Base64#GZIP
765      * @see Base64#DO_BREAK_LINES
766      * @throws java.io.IOException
767      *             if there is an error
768      * @throws NullPointerException
769      *             if source array is null
770      * @throws IllegalArgumentException
771      *             if source array, offset, or length are invalid
772      * @since 2.0
773      */
774     public static String encodeBytes(byte[] source, int off, int len, int options) throws java.io.IOException {
775         byte[] encoded = encodeBytesToBytes(source, off, len, options);
776 
777         // Return value according to relevant encoding.
778         try {
779             return new String(encoded, PREFERRED_ENCODING);
780         } // end try
781         catch (java.io.UnsupportedEncodingException uue) {
782             return new String(encoded);
783         } // end catch
784 
785     } // end encodeBytes
786 
787     /**
788      * Similar to {@link #encodeBytes(byte[])} but returns a byte array instead
789      * of instantiating a String. This is more efficient if you're working with
790      * I/O streams and have large data sets to encode.
791      * 
792      * 
793      * @param source
794      *            The data to convert
795      * @return The Base64-encoded data as a byte[] (of ASCII characters)
796      * @throws NullPointerException
797      *             if source array is null
798      * @since 2.3.1
799      */
800     public static byte[] encodeBytesToBytes(byte[] source) {
801         byte[] encoded = null;
802         try {
803             encoded = encodeBytesToBytes(source, 0, source.length, Base64.NO_OPTIONS);
804         } catch (java.io.IOException ex) {
805             assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
806         }
807         return encoded;
808     }
809 
810     /**
811      * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns a byte
812      * array instead of instantiating a String. This is more efficient if you're
813      * working with I/O streams and have large data sets to encode.
814      * 
815      * 
816      * @param source
817      *            The data to convert
818      * @param off
819      *            Offset in array where conversion should begin
820      * @param len
821      *            Length of data to convert
822      * @param options
823      *            Specified options
824      * @return The Base64-encoded data as a String
825      * @see Base64#GZIP
826      * @see Base64#DO_BREAK_LINES
827      * @throws java.io.IOException
828      *             if there is an error
829      * @throws NullPointerException
830      *             if source array is null
831      * @throws IllegalArgumentException
832      *             if source array, offset, or length are invalid
833      * @since 2.3.1
834      */
835     public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) throws java.io.IOException {
836 
837         if (source == null) {
838             throw new NullPointerException("Cannot serialize a null array.");
839         } // end if: null
840 
841         if (off < 0) {
842             throw new IllegalArgumentException("Cannot have negative offset: " + off);
843         } // end if: off < 0
844 
845         if (len < 0) {
846             throw new IllegalArgumentException("Cannot have length offset: " + len);
847         } // end if: len < 0
848 
849         if (off + len > source.length) {
850             throw new IllegalArgumentException(String.format(
851                     "Cannot have offset of %d and length of %d with array of length %d", off, len, source.length));
852         } // end if: off < 0
853 
854         // Compress?
855         if ((options & GZIP) != 0) {
856             java.io.ByteArrayOutputStream baos = null;
857             java.util.zip.GZIPOutputStream gzos = null;
858             Base64.OutputStream b64os = null;
859 
860             try {
861                 // GZip -> Base64 -> ByteArray
862                 baos = new java.io.ByteArrayOutputStream();
863                 b64os = new Base64.OutputStream(baos, ENCODE | options);
864                 gzos = new java.util.zip.GZIPOutputStream(b64os);
865 
866                 gzos.write(source, off, len);
867                 gzos.close();
868             } // end try
869             catch (java.io.IOException e) {
870                 // Catch it and then throw it immediately so that
871                 // the finally{} block is called for cleanup.
872                 throw e;
873             } // end catch
874             finally {
875                 try {
876                     gzos.close();
877                 } catch (Exception e) {
878                 }
879                 try {
880                     b64os.close();
881                 } catch (Exception e) {
882                 }
883                 try {
884                     baos.close();
885                 } catch (Exception e) {
886                 }
887             } // end finally
888 
889             return baos.toByteArray();
890         } // end if: compress
891 
892         // Else, don't compress. Better not to use streams at all then.
893         else {
894             boolean breakLines = (options & DO_BREAK_LINES) != 0;
895 
896             // int len43 = len * 4 / 3;
897             // byte[] outBuff = new byte[ ( len43 ) // Main 4:3
898             // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
899             // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
900             // Try to determine more precisely how big the array needs to be.
901             // If we get it right, we don't have to do an array copy, and
902             // we save a bunch of memory.
903             int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed
904                                                                 // for actual
905                                                                 // encoding
906             if (breakLines) {
907                 encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline
908                                                     // characters
909             }
910             byte[] outBuff = new byte[encLen];
911 
912             int d = 0;
913             int e = 0;
914             int len2 = len - 2;
915             int lineLength = 0;
916             for (; d < len2; d += 3, e += 4) {
917                 encode3to4(source, d + off, 3, outBuff, e, options);
918 
919                 lineLength += 4;
920                 if (breakLines && lineLength >= MAX_LINE_LENGTH) {
921                     outBuff[e + 4] = NEW_LINE;
922                     e++;
923                     lineLength = 0;
924                 } // end if: end of line
925             } // en dfor: each piece of array
926 
927             if (d < len) {
928                 encode3to4(source, d + off, len - d, outBuff, e, options);
929                 e += 4;
930             } // end if: some padding needed
931 
932             // Only resize array if we didn't guess it right.
933             if (e <= outBuff.length - 1) {
934                 // If breaking lines and the last byte falls right at
935                 // the line length (76 bytes per line), there will be
936                 // one extra byte, and the array will need to be resized.
937                 // Not too bad of an estimate on array size, I'd say.
938                 byte[] finalOut = new byte[e];
939                 System.arraycopy(outBuff, 0, finalOut, 0, e);
940                 // System.err.println("Having to resize array from " +
941                 // outBuff.length + " to " + e );
942                 return finalOut;
943             } else {
944                 // System.err.println("No need to resize array.");
945                 return outBuff;
946             }
947 
948         } // end else: don't compress
949 
950     } // end encodeBytesToBytes
951 
952     /* ******** D E C O D I N G M E T H O D S ******** */
953 
954     /**
955      * Decodes four bytes from array <var>source</var> and writes the resulting
956      * bytes (up to three of them) to <var>destination</var>. The source and
957      * destination arrays can be manipulated anywhere along their length by
958      * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
959      * does not check to make sure your arrays are large enough to accomodate
960      * <var>srcOffset</var> + 4 for the <var>source</var> array or
961      * <var>destOffset</var> + 3 for the <var>destination</var> array. This
962      * method returns the actual number of bytes that were converted from the
963      * Base64 encoding.
964      * <p>
965      * This is the lowest level of the decoding methods with all possible
966      * parameters.
967      * </p>
968      * 
969      * 
970      * @param source
971      *            the array to convert
972      * @param srcOffset
973      *            the index where conversion begins
974      * @param destination
975      *            the array to hold the conversion
976      * @param destOffset
977      *            the index where output will be put
978      * @param options
979      *            alphabet type is pulled from this (standard, url-safe,
980      *            ordered)
981      * @return the number of decoded bytes converted
982      * @throws NullPointerException
983      *             if source or destination arrays are null
984      * @throws IllegalArgumentException
985      *             if srcOffset or destOffset are invalid or there is not enough
986      *             room in the array.
987      * @since 1.3
988      */
989     private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset, int options) {
990 
991         // Lots of error checking and exception throwing
992         if (source == null) {
993             throw new NullPointerException("Source array was null.");
994         } // end if
995         if (destination == null) {
996             throw new NullPointerException("Destination array was null.");
997         } // end if
998         if (srcOffset < 0 || srcOffset + 3 >= source.length) {
999             throw new IllegalArgumentException(String.format(
1000                     "Source array with length %d cannot have offset of %d and still process four bytes.",
1001                     source.length, srcOffset));
1002         } // end if
1003         if (destOffset < 0 || destOffset + 2 >= destination.length) {
1004             throw new IllegalArgumentException(String.format(
1005                     "Destination array with length %d cannot have offset of %d and still store three bytes.",
1006                     destination.length, destOffset));
1007         } // end if
1008 
1009         byte[] DECODABET = getDecodabet(options);
1010 
1011         // Example: Dk==
1012         if (source[srcOffset + 2] == EQUALS_SIGN) {
1013             // Two ways to do the same thing. Don't know which way I like best.
1014             // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
1015             // )
1016             // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
1017             int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
1018                     | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
1019 
1020             destination[destOffset] = (byte) (outBuff >>> 16);
1021             return 1;
1022         }
1023 
1024         // Example: DkL=
1025         else if (source[srcOffset + 3] == EQUALS_SIGN) {
1026             // Two ways to do the same thing. Don't know which way I like best.
1027             // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
1028             // )
1029             // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1030             // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
1031             int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
1032                     | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
1033                     | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
1034 
1035             destination[destOffset] = (byte) (outBuff >>> 16);
1036             destination[destOffset + 1] = (byte) (outBuff >>> 8);
1037             return 2;
1038         }
1039 
1040         // Example: DkLE
1041         else {
1042             // Two ways to do the same thing. Don't know which way I like best.
1043             // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
1044             // )
1045             // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1046             // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
1047             // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
1048             int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
1049                     | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
1050                     | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF));
1051 
1052             destination[destOffset] = (byte) (outBuff >> 16);
1053             destination[destOffset + 1] = (byte) (outBuff >> 8);
1054             destination[destOffset + 2] = (byte) (outBuff);
1055 
1056             return 3;
1057         }
1058     } // end decodeToBytes
1059 
1060     /**
1061      * Low-level access to decoding ASCII characters in the form of a byte
1062      * array. <strong>Ignores GUNZIP option, if it's set.</strong> This is not
1063      * generally a recommended method, although it is used internally as part of
1064      * the decoding process. Special case: if len = 0, an empty array is
1065      * returned. Still, if you need more speed and reduced memory footprint (and
1066      * aren't gzipping), consider this method.
1067      * 
1068      * @param source
1069      *            The Base64 encoded data
1070      * @return decoded data
1071      * @since 2.3.1
1072      */
1073     public static byte[] decode(byte[] source) throws java.io.IOException {
1074         byte[] decoded = null;
1075         // try {
1076         decoded = decode(source, 0, source.length, Base64.NO_OPTIONS);
1077         // } catch( java.io.IOException ex ) {
1078         // assert false :
1079         // "IOExceptions only come from GZipping, which is turned off: " +
1080         // ex.getMessage();
1081         // }
1082         return decoded;
1083     }
1084 
1085     /**
1086      * Low-level access to decoding ASCII characters in the form of a byte
1087      * array. <strong>Ignores GUNZIP option, if it's set.</strong> This is not
1088      * generally a recommended method, although it is used internally as part of
1089      * the decoding process. Special case: if len = 0, an empty array is
1090      * returned. Still, if you need more speed and reduced memory footprint (and
1091      * aren't gzipping), consider this method.
1092      * 
1093      * @param source
1094      *            The Base64 encoded data
1095      * @param off
1096      *            The offset of where to begin decoding
1097      * @param len
1098      *            The length of characters to decode
1099      * @param options
1100      *            Can specify options such as alphabet type to use
1101      * @return decoded data
1102      * @throws java.io.IOException
1103      *             If bogus characters exist in source data
1104      * @since 1.3
1105      */
1106     public static byte[] decode(byte[] source, int off, int len, int options) throws java.io.IOException {
1107 
1108         // Lots of error checking and exception throwing
1109         if (source == null) {
1110             throw new NullPointerException("Cannot decode null source array.");
1111         } // end if
1112         if (off < 0 || off + len > source.length) {
1113             throw new IllegalArgumentException(String.format(
1114                     "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off,
1115                     len));
1116         } // end if
1117 
1118         if (len == 0) {
1119             return new byte[0];
1120         } else if (len < 4) {
1121             throw new IllegalArgumentException(
1122                     "Base64-encoded string must have at least four characters, but length specified was " + len);
1123         } // end if
1124 
1125         byte[] DECODABET = getDecodabet(options);
1126 
1127         int len34 = len * 3 / 4; // Estimate on array size
1128         byte[] outBuff = new byte[len34]; // Upper limit on size of output
1129         int outBuffPosn = 0; // Keep track of where we're writing
1130 
1131         byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating
1132                                  // white space
1133         int b4Posn = 0; // Keep track of four byte input buffer
1134         int i = 0; // Source array counter
1135         byte sbiDecode = 0; // Special value from DECODABET
1136 
1137         for (i = off; i < off + len; i++) { // Loop through source
1138 
1139             sbiDecode = DECODABET[source[i] & 0xFF];
1140 
1141             // White space, Equals sign, or legit Base64 character
1142             // Note the values such as -5 and -9 in the
1143             // DECODABETs at the top of the file.
1144             if (sbiDecode >= WHITE_SPACE_ENC) {
1145                 if (sbiDecode >= EQUALS_SIGN_ENC) {
1146                     b4[b4Posn++] = source[i]; // Save non-whitespace
1147                     if (b4Posn > 3) { // Time to decode?
1148                         outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
1149                         b4Posn = 0;
1150 
1151                         // If that was the equals sign, break out of 'for' loop
1152                         if (source[i] == EQUALS_SIGN) {
1153                             break;
1154                         } // end if: equals sign
1155                     } // end if: quartet built
1156                 } // end if: equals sign or better
1157             } // end if: white space, equals sign or better
1158             else {
1159                 // There's a bad input character in the Base64 stream.
1160                 throw new java.io.IOException(String.format(
1161                         "Bad Base64 input character decimal %d in array position %d", ((int) source[i]) & 0xFF, i));
1162             } // end else:
1163         } // each input character
1164 
1165         byte[] out = new byte[outBuffPosn];
1166         System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
1167         return out;
1168     } // end decode
1169 
1170     /**
1171      * Decodes data from Base64 notation, automatically detecting
1172      * gzip-compressed data and decompressing it.
1173      * 
1174      * @param s
1175      *            the string to decode
1176      * @return the decoded data
1177      * @throws java.io.IOException
1178      *             If there is a problem
1179      * @since 1.4
1180      */
1181     public static byte[] decode(String s) throws java.io.IOException {
1182         return decode(s, NO_OPTIONS);
1183     }
1184 
1185     /**
1186      * Decodes data from Base64 notation, automatically detecting
1187      * gzip-compressed data and decompressing it.
1188      * 
1189      * @param s
1190      *            the string to decode
1191      * @param options
1192      *            encode options such as URL_SAFE
1193      * @return the decoded data
1194      * @throws java.io.IOException
1195      *             if there is an error
1196      * @throws NullPointerException
1197      *             if <tt>s</tt> is null
1198      * @since 1.4
1199      */
1200     public static byte[] decode(String s, int options) throws java.io.IOException {
1201 
1202         if (s == null) {
1203             throw new NullPointerException("Input string was null.");
1204         } // end if
1205 
1206         byte[] bytes;
1207         try {
1208             bytes = s.getBytes(PREFERRED_ENCODING);
1209         } // end try
1210         catch (java.io.UnsupportedEncodingException uee) {
1211             bytes = s.getBytes();
1212         } // end catch
1213           // </change>
1214 
1215         // Decode
1216         bytes = decode(bytes, 0, bytes.length, options);
1217 
1218         // Check to see if it's gzip-compressed
1219         // GZIP Magic Two-Byte Number: 0x8b1f (35615)
1220         boolean dontGunzip = (options & DONT_GUNZIP) != 0;
1221         if ((bytes != null) && (bytes.length >= 4) && (!dontGunzip)) {
1222 
1223             int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
1224             if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
1225                 java.io.ByteArrayInputStream bais = null;
1226                 java.util.zip.GZIPInputStream gzis = null;
1227                 java.io.ByteArrayOutputStream baos = null;
1228                 byte[] buffer = new byte[2048];
1229                 int length = 0;
1230 
1231                 try {
1232                     baos = new java.io.ByteArrayOutputStream();
1233                     bais = new java.io.ByteArrayInputStream(bytes);
1234                     gzis = new java.util.zip.GZIPInputStream(bais);
1235 
1236                     while ((length = gzis.read(buffer)) >= 0) {
1237                         baos.write(buffer, 0, length);
1238                     } // end while: reading input
1239 
1240                     // No error? Get new bytes.
1241                     bytes = baos.toByteArray();
1242 
1243                 } // end try
1244                 catch (java.io.IOException e) {
1245                     e.printStackTrace();
1246                     // Just return originally-decoded bytes
1247                 } // end catch
1248                 finally {
1249                     try {
1250                         baos.close();
1251                     } catch (Exception e) {
1252                     }
1253                     try {
1254                         gzis.close();
1255                     } catch (Exception e) {
1256                     }
1257                     try {
1258                         bais.close();
1259                     } catch (Exception e) {
1260                     }
1261                 } // end finally
1262 
1263             } // end if: gzipped
1264         } // end if: bytes.length >= 2
1265 
1266         return bytes;
1267     } // end decode
1268 
1269     /**
1270      * Attempts to decode Base64 data and deserialize a Java Object within.
1271      * Returns <tt>null</tt> if there was an error.
1272      * 
1273      * @param encodedObject
1274      *            The Base64 data to decode
1275      * @return The decoded and deserialized object
1276      * @throws NullPointerException
1277      *             if encodedObject is null
1278      * @throws java.io.IOException
1279      *             if there is a general error
1280      * @throws ClassNotFoundException
1281      *             if the decoded object is of a class that cannot be found by
1282      *             the JVM
1283      * @since 1.5
1284      */
1285     public static Object decodeToObject(String encodedObject) throws java.io.IOException,
1286             java.lang.ClassNotFoundException {
1287         return decodeToObject(encodedObject, NO_OPTIONS, null);
1288     }
1289 
1290     /**
1291      * Attempts to decode Base64 data and deserialize a Java Object within.
1292      * Returns <tt>null</tt> if there was an error. If <tt>loader</tt> is not
1293      * null, it will be the class loader used when deserializing.
1294      * 
1295      * @param encodedObject
1296      *            The Base64 data to decode
1297      * @param options
1298      *            Various parameters related to decoding
1299      * @param loader
1300      *            Optional class loader to use in deserializing classes.
1301      * @return The decoded and deserialized object
1302      * @throws NullPointerException
1303      *             if encodedObject is null
1304      * @throws java.io.IOException
1305      *             if there is a general error
1306      * @throws ClassNotFoundException
1307      *             if the decoded object is of a class that cannot be found by
1308      *             the JVM
1309      * @since 2.3.4
1310      */
1311     public static Object decodeToObject(String encodedObject, int options, final ClassLoader loader)
1312             throws java.io.IOException, java.lang.ClassNotFoundException {
1313 
1314         // Decode and gunzip if necessary
1315         byte[] objBytes = decode(encodedObject, options);
1316 
1317         java.io.ByteArrayInputStream bais = null;
1318         java.io.ObjectInputStream ois = null;
1319         Object obj = null;
1320 
1321         try {
1322             bais = new java.io.ByteArrayInputStream(objBytes);
1323 
1324             // If no custom class loader is provided, use Java's builtin OIS.
1325             if (loader == null) {
1326                 ois = new java.io.ObjectInputStream(bais);
1327             } // end if: no loader provided
1328 
1329             // Else make a customized object input stream that uses
1330             // the provided class loader.
1331             else {
1332                 ois = new java.io.ObjectInputStream(bais) {
1333                     @Override
1334                     public Class<?> resolveClass(java.io.ObjectStreamClass streamClass) throws java.io.IOException,
1335                             ClassNotFoundException {
1336                         Class<?> c = Class.forName(streamClass.getName(), false, loader);
1337                         if (c == null) {
1338                             return super.resolveClass(streamClass);
1339                         } else {
1340                             return c; // Class loader knows of this class.
1341                         } // end else: not null
1342                     } // end resolveClass
1343                 }; // end ois
1344             } // end else: no custom class loader
1345 
1346             obj = ois.readObject();
1347         } // end try
1348         catch (java.io.IOException e) {
1349             throw e; // Catch and throw in order to execute finally{}
1350         } // end catch
1351         catch (java.lang.ClassNotFoundException e) {
1352             throw e; // Catch and throw in order to execute finally{}
1353         } // end catch
1354         finally {
1355             try {
1356                 bais.close();
1357             } catch (Exception e) {
1358             }
1359             try {
1360                 ois.close();
1361             } catch (Exception e) {
1362             }
1363         } // end finally
1364 
1365         return obj;
1366     } // end decodeObject
1367 
1368     /**
1369      * Convenience method for encoding data to a file.
1370      * 
1371      * <p>
1372      * As of v 2.3, if there is a error, the method will throw an
1373      * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
1374      * just returned false, but in retrospect that's a pretty poor way to handle
1375      * it.
1376      * </p>
1377      * 
1378      * @param dataToEncode
1379      *            byte array of data to encode in base64 form
1380      * @param filename
1381      *            Filename for saving encoded data
1382      * @throws java.io.IOException
1383      *             if there is an error
1384      * @throws NullPointerException
1385      *             if dataToEncode is null
1386      * @since 2.1
1387      */
1388     public static void encodeToFile(byte[] dataToEncode, String filename) throws java.io.IOException {
1389 
1390         if (dataToEncode == null) {
1391             throw new NullPointerException("Data to encode was null.");
1392         } // end iff
1393 
1394         Base64.OutputStream bos = null;
1395         try {
1396             bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.ENCODE);
1397             bos.write(dataToEncode);
1398         } // end try
1399         catch (java.io.IOException e) {
1400             throw e; // Catch and throw to execute finally{} block
1401         } // end catch: java.io.IOException
1402         finally {
1403             try {
1404                 bos.close();
1405             } catch (Exception e) {
1406             }
1407         } // end finally
1408 
1409     } // end encodeToFile
1410 
1411     /**
1412      * Convenience method for decoding data to a file.
1413      * 
1414      * <p>
1415      * As of v 2.3, if there is a error, the method will throw an
1416      * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
1417      * just returned false, but in retrospect that's a pretty poor way to handle
1418      * it.
1419      * </p>
1420      * 
1421      * @param dataToDecode
1422      *            Base64-encoded data as a string
1423      * @param filename
1424      *            Filename for saving decoded data
1425      * @throws java.io.IOException
1426      *             if there is an error
1427      * @since 2.1
1428      */
1429     public static void decodeToFile(String dataToDecode, String filename) throws java.io.IOException {
1430 
1431         Base64.OutputStream bos = null;
1432         try {
1433             bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.DECODE);
1434             bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
1435         } // end try
1436         catch (java.io.IOException e) {
1437             throw e; // Catch and throw to execute finally{} block
1438         } // end catch: java.io.IOException
1439         finally {
1440             try {
1441                 bos.close();
1442             } catch (Exception e) {
1443             }
1444         } // end finally
1445 
1446     } // end decodeToFile
1447 
1448     /**
1449      * Convenience method for reading a base64-encoded file and decoding it.
1450      * 
1451      * <p>
1452      * As of v 2.3, if there is a error, the method will throw an
1453      * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
1454      * just returned false, but in retrospect that's a pretty poor way to handle
1455      * it.
1456      * </p>
1457      * 
1458      * @param filename
1459      *            Filename for reading encoded data
1460      * @return decoded byte array
1461      * @throws java.io.IOException
1462      *             if there is an error
1463      * @since 2.1
1464      */
1465     public static byte[] decodeFromFile(String filename) throws java.io.IOException {
1466 
1467         byte[] decodedData = null;
1468         Base64.InputStream bis = null;
1469         try {
1470             // Set up some useful variables
1471             java.io.File file = new java.io.File(filename);
1472             byte[] buffer = null;
1473             int length = 0;
1474             int numBytes = 0;
1475 
1476             // Check for size of file
1477             if (file.length() > Integer.MAX_VALUE) {
1478                 throw new java.io.IOException("File is too big for this convenience method (" + file.length()
1479                         + " bytes).");
1480             } // end if: file too big for int index
1481             buffer = new byte[(int) file.length()];
1482 
1483             // Open a stream
1484             bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)),
1485                     Base64.DECODE);
1486 
1487             // Read until done
1488             while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
1489                 length += numBytes;
1490             } // end while
1491 
1492             // Save in a variable to return
1493             decodedData = new byte[length];
1494             System.arraycopy(buffer, 0, decodedData, 0, length);
1495 
1496         } // end try
1497         catch (java.io.IOException e) {
1498             throw e; // Catch and release to execute finally{}
1499         } // end catch: java.io.IOException
1500         finally {
1501             try {
1502                 bis.close();
1503             } catch (Exception e) {
1504             }
1505         } // end finally
1506 
1507         return decodedData;
1508     } // end decodeFromFile
1509 
1510     /**
1511      * Convenience method for reading a binary file and base64-encoding it.
1512      * 
1513      * <p>
1514      * As of v 2.3, if there is a error, the method will throw an
1515      * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
1516      * just returned false, but in retrospect that's a pretty poor way to handle
1517      * it.
1518      * </p>
1519      * 
1520      * @param filename
1521      *            Filename for reading binary data
1522      * @return base64-encoded string
1523      * @throws java.io.IOException
1524      *             if there is an error
1525      * @since 2.1
1526      */
1527     public static String encodeFromFile(String filename) throws java.io.IOException {
1528 
1529         String encodedData = null;
1530         Base64.InputStream bis = null;
1531         try {
1532             // Set up some useful variables
1533             java.io.File file = new java.io.File(filename);
1534             byte[] buffer = new byte[Math.max((int) (file.length() * 1.4 + 1), 40)]; // Need
1535                                                                                      // max()
1536                                                                                      // for
1537                                                                                      // math
1538                                                                                      // on
1539                                                                                      // small
1540                                                                                      // files
1541                                                                                      // (v2.2.1);
1542                                                                                      // Need
1543                                                                                      // +1
1544                                                                                      // for
1545                                                                                      // a
1546                                                                                      // few
1547                                                                                      // corner
1548                                                                                      // cases
1549                                                                                      // (v2.3.5)
1550             int length = 0;
1551             int numBytes = 0;
1552 
1553             // Open a stream
1554             bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)),
1555                     Base64.ENCODE);
1556 
1557             // Read until done
1558             while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
1559                 length += numBytes;
1560             } // end while
1561 
1562             // Save in a variable to return
1563             encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING);
1564 
1565         } // end try
1566         catch (java.io.IOException e) {
1567             throw e; // Catch and release to execute finally{}
1568         } // end catch: java.io.IOException
1569         finally {
1570             try {
1571                 bis.close();
1572             } catch (Exception e) {
1573             }
1574         } // end finally
1575 
1576         return encodedData;
1577     } // end encodeFromFile
1578 
1579     /**
1580      * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1581      * 
1582      * @param infile
1583      *            Input file
1584      * @param outfile
1585      *            Output file
1586      * @throws java.io.IOException
1587      *             if there is an error
1588      * @since 2.2
1589      */
1590     public static void encodeFileToFile(String infile, String outfile) throws java.io.IOException {
1591 
1592         String encoded = Base64.encodeFromFile(infile);
1593         java.io.OutputStream out = null;
1594         try {
1595             out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
1596             out.write(encoded.getBytes("US-ASCII")); // Strict, 7-bit output.
1597         } // end try
1598         catch (java.io.IOException e) {
1599             throw e; // Catch and release to execute finally{}
1600         } // end catch
1601         finally {
1602             try {
1603                 out.close();
1604             } catch (Exception ex) {
1605             }
1606         } // end finally
1607     } // end encodeFileToFile
1608 
1609     /**
1610      * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1611      * 
1612      * @param infile
1613      *            Input file
1614      * @param outfile
1615      *            Output file
1616      * @throws java.io.IOException
1617      *             if there is an error
1618      * @since 2.2
1619      */
1620     public static void decodeFileToFile(String infile, String outfile) throws java.io.IOException {
1621 
1622         byte[] decoded = Base64.decodeFromFile(infile);
1623         java.io.OutputStream out = null;
1624         try {
1625             out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
1626             out.write(decoded);
1627         } // end try
1628         catch (java.io.IOException e) {
1629             throw e; // Catch and release to execute finally{}
1630         } // end catch
1631         finally {
1632             try {
1633                 out.close();
1634             } catch (Exception ex) {
1635             }
1636         } // end finally
1637     } // end decodeFileToFile
1638 
1639     /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
1640 
1641     /**
1642      * A {@link Base64.InputStream} will read data from another
1643      * <tt>java.io.InputStream</tt>, given in the constructor, and encode/decode
1644      * to/from Base64 notation on the fly.
1645      * 
1646      * @see Base64
1647      * @since 1.3
1648      */
1649     public static class InputStream extends java.io.FilterInputStream {
1650 
1651         private boolean encode; // Encoding or decoding
1652         private int position; // Current position in the buffer
1653         private byte[] buffer; // Small buffer holding converted data
1654         private int bufferLength; // Length of buffer (3 or 4)
1655         private int numSigBytes; // Number of meaningful bytes in the buffer
1656         private int lineLength;
1657         private boolean breakLines; // Break lines at less than 80 characters
1658         private int options; // Record options used to create the stream.
1659         private byte[] decodabet; // Local copies to avoid extra method calls
1660 
1661         /**
1662          * Constructs a {@link Base64.InputStream} in DECODE mode.
1663          * 
1664          * @param in
1665          *            the <tt>java.io.InputStream</tt> from which to read data.
1666          * @since 1.3
1667          */
1668         public InputStream(java.io.InputStream in) {
1669             this(in, DECODE);
1670         } // end constructor
1671 
1672         /**
1673          * Constructs a {@link Base64.InputStream} in either ENCODE or DECODE
1674          * mode.
1675          * <p>
1676          * Valid options:
1677          * 
1678          * <pre>
1679          *   ENCODE or DECODE: Encode or Decode as data is read.
1680          *   DO_BREAK_LINES: break lines at 76 characters
1681          *     (only meaningful when encoding)</i>
1682          * </pre>
1683          * <p>
1684          * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1685          * 
1686          * 
1687          * @param in
1688          *            the <tt>java.io.InputStream</tt> from which to read data.
1689          * @param options
1690          *            Specified options
1691          * @see Base64#ENCODE
1692          * @see Base64#DECODE
1693          * @see Base64#DO_BREAK_LINES
1694          * @since 2.0
1695          */
1696         public InputStream(java.io.InputStream in, int options) {
1697 
1698             super(in);
1699             this.options = options; // Record for later
1700             this.breakLines = (options & DO_BREAK_LINES) > 0;
1701             this.encode = (options & ENCODE) > 0;
1702             this.bufferLength = encode ? 4 : 3;
1703             this.buffer = new byte[bufferLength];
1704             this.position = -1;
1705             this.lineLength = 0;
1706             this.decodabet = getDecodabet(options);
1707         } // end constructor
1708 
1709         /**
1710          * Reads enough of the input stream to convert to/from Base64 and
1711          * returns the next byte.
1712          * 
1713          * @return next byte
1714          * @since 1.3
1715          */
1716         @Override
1717         public int read() throws java.io.IOException {
1718 
1719             // Do we need to get data?
1720             if (position < 0) {
1721                 if (encode) {
1722                     byte[] b3 = new byte[3];
1723                     int numBinaryBytes = 0;
1724                     for (int i = 0; i < 3; i++) {
1725                         int b = in.read();
1726 
1727                         // If end of stream, b is -1.
1728                         if (b >= 0) {
1729                             b3[i] = (byte) b;
1730                             numBinaryBytes++;
1731                         } else {
1732                             break; // out of for loop
1733                         } // end else: end of stream
1734 
1735                     } // end for: each needed input byte
1736 
1737                     if (numBinaryBytes > 0) {
1738                         encode3to4(b3, 0, numBinaryBytes, buffer, 0, options);
1739                         position = 0;
1740                         numSigBytes = 4;
1741                     } // end if: got data
1742                     else {
1743                         return -1; // Must be end of stream
1744                     } // end else
1745                 } // end if: encoding
1746 
1747                 // Else decoding
1748                 else {
1749                     byte[] b4 = new byte[4];
1750                     int i = 0;
1751                     for (i = 0; i < 4; i++) {
1752                         // Read four "meaningful" bytes:
1753                         int b = 0;
1754                         do {
1755                             b = in.read();
1756                         } while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC);
1757 
1758                         if (b < 0) {
1759                             break; // Reads a -1 if end of stream
1760                         } // end if: end of stream
1761 
1762                         b4[i] = (byte) b;
1763                     } // end for: each needed input byte
1764 
1765                     if (i == 4) {
1766                         numSigBytes = decode4to3(b4, 0, buffer, 0, options);
1767                         position = 0;
1768                     } // end if: got four characters
1769                     else if (i == 0) {
1770                         return -1;
1771                     } // end else if: also padded correctly
1772                     else {
1773                         // Must have broken out from above.
1774                         throw new java.io.IOException("Improperly padded Base64 input.");
1775                     } // end
1776 
1777                 } // end else: decode
1778             } // end else: get data
1779 
1780             // Got data?
1781             if (position >= 0) {
1782                 // End of relevant data?
1783                 if ( /* !encode && */position >= numSigBytes) {
1784                     return -1;
1785                 } // end if: got data
1786 
1787                 if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
1788                     lineLength = 0;
1789                     return '\n';
1790                 } // end if
1791                 else {
1792                     lineLength++; // This isn't important when decoding
1793                                   // but throwing an extra "if" seems
1794                                   // just as wasteful.
1795 
1796                     int b = buffer[position++];
1797 
1798                     if (position >= bufferLength) {
1799                         position = -1;
1800                     } // end if: end
1801 
1802                     return b & 0xFF; // This is how you "cast" a byte that's
1803                                      // intended to be unsigned.
1804                 } // end else
1805             } // end if: position >= 0
1806 
1807             // Else error
1808             else {
1809                 throw new java.io.IOException("Error in Base64 code reading stream.");
1810             } // end else
1811         } // end read
1812 
1813         /**
1814          * Calls {@link #read()} repeatedly until the end of stream is reached
1815          * or <var>len</var> bytes are read. Returns number of bytes read into
1816          * array or -1 if end of stream is encountered.
1817          * 
1818          * @param dest
1819          *            array to hold values
1820          * @param off
1821          *            offset for array
1822          * @param len
1823          *            max number of bytes to read into array
1824          * @return bytes read into array or -1 if end of stream is encountered.
1825          * @since 1.3
1826          */
1827         @Override
1828         public int read(byte[] dest, int off, int len) throws java.io.IOException {
1829             int i;
1830             int b;
1831             for (i = 0; i < len; i++) {
1832                 b = read();
1833 
1834                 if (b >= 0) {
1835                     dest[off + i] = (byte) b;
1836                 } else if (i == 0) {
1837                     return -1;
1838                 } else {
1839                     break; // Out of 'for' loop
1840                 } // Out of 'for' loop
1841             } // end for: each byte read
1842             return i;
1843         } // end read
1844 
1845     } // end inner class InputStream
1846 
1847     /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1848 
1849     /**
1850      * A {@link Base64.OutputStream} will write data to another
1851      * <tt>java.io.OutputStream</tt>, given in the constructor, and
1852      * encode/decode to/from Base64 notation on the fly.
1853      * 
1854      * @see Base64
1855      * @since 1.3
1856      */
1857     public static class OutputStream extends java.io.FilterOutputStream {
1858 
1859         private boolean encode;
1860         private int position;
1861         private byte[] buffer;
1862         private int bufferLength;
1863         private int lineLength;
1864         private boolean breakLines;
1865         private byte[] b4; // Scratch used in a few places
1866         private boolean suspendEncoding;
1867         private int options; // Record for later
1868         private byte[] decodabet; // Local copies to avoid extra method calls
1869 
1870         /**
1871          * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1872          * 
1873          * @param out
1874          *            the <tt>java.io.OutputStream</tt> to which data will be
1875          *            written.
1876          * @since 1.3
1877          */
1878         public OutputStream(java.io.OutputStream out) {
1879             this(out, ENCODE);
1880         } // end constructor
1881 
1882         /**
1883          * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE
1884          * mode.
1885          * <p>
1886          * Valid options:
1887          * 
1888          * <pre>
1889          *   ENCODE or DECODE: Encode or Decode as data is read.
1890          *   DO_BREAK_LINES: don't break lines at 76 characters
1891          *     (only meaningful when encoding)</i>
1892          * </pre>
1893          * <p>
1894          * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1895          * 
1896          * @param out
1897          *            the <tt>java.io.OutputStream</tt> to which data will be
1898          *            written.
1899          * @param options
1900          *            Specified options.
1901          * @see Base64#ENCODE
1902          * @see Base64#DECODE
1903          * @see Base64#DO_BREAK_LINES
1904          * @since 1.3
1905          */
1906         public OutputStream(java.io.OutputStream out, int options) {
1907             super(out);
1908             this.breakLines = (options & DO_BREAK_LINES) != 0;
1909             this.encode = (options & ENCODE) != 0;
1910             this.bufferLength = encode ? 3 : 4;
1911             this.buffer = new byte[bufferLength];
1912             this.position = 0;
1913             this.lineLength = 0;
1914             this.suspendEncoding = false;
1915             this.b4 = new byte[4];
1916             this.options = options;
1917             this.decodabet = getDecodabet(options);
1918         } // end constructor
1919 
1920         /**
1921          * Writes the byte to the output stream after converting to/from Base64
1922          * notation. When encoding, bytes are buffered three at a time before
1923          * the output stream actually gets a write() call. When decoding, bytes
1924          * are buffered four at a time.
1925          * 
1926          * @param theByte
1927          *            the byte to write
1928          * @since 1.3
1929          */
1930         @Override
1931         public void write(int theByte) throws java.io.IOException {
1932             // Encoding suspended?
1933             if (suspendEncoding) {
1934                 this.out.write(theByte);
1935                 return;
1936             } // end if: supsended
1937 
1938             // Encode?
1939             if (encode) {
1940                 buffer[position++] = (byte) theByte;
1941                 if (position >= bufferLength) { // Enough to encode.
1942 
1943                     this.out.write(encode3to4(b4, buffer, bufferLength, options));
1944 
1945                     lineLength += 4;
1946                     if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1947                         this.out.write(NEW_LINE);
1948                         lineLength = 0;
1949                     } // end if: end of line
1950 
1951                     position = 0;
1952                 } // end if: enough to output
1953             } // end if: encoding
1954 
1955             // Else, Decoding
1956             else {
1957                 // Meaningful Base64 character?
1958                 if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
1959                     buffer[position++] = (byte) theByte;
1960                     if (position >= bufferLength) { // Enough to output.
1961 
1962                         int len = Base64.decode4to3(buffer, 0, b4, 0, options);
1963                         out.write(b4, 0, len);
1964                         position = 0;
1965                     } // end if: enough to output
1966                 } // end if: meaningful base64 character
1967                 else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
1968                     throw new java.io.IOException("Invalid character in Base64 data.");
1969                 } // end else: not white space either
1970             } // end else: decoding
1971         } // end write
1972 
1973         /**
1974          * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are
1975          * written.
1976          * 
1977          * @param theBytes
1978          *            array from which to read bytes
1979          * @param off
1980          *            offset for array
1981          * @param len
1982          *            max number of bytes to read into array
1983          * @since 1.3
1984          */
1985         @Override
1986         public void write(byte[] theBytes, int off, int len) throws java.io.IOException {
1987             // Encoding suspended?
1988             if (suspendEncoding) {
1989                 this.out.write(theBytes, off, len);
1990                 return;
1991             } // end if: supsended
1992 
1993             for (int i = 0; i < len; i++) {
1994                 write(theBytes[off + i]);
1995             } // end for: each byte written
1996 
1997         } // end write
1998 
1999         /**
2000          * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer
2001          * without closing the stream.
2002          * 
2003          * @throws java.io.IOException
2004          *             if there's an error.
2005          */
2006         public void flushBase64() throws java.io.IOException {
2007             if (position > 0) {
2008                 if (encode) {
2009                     out.write(encode3to4(b4, buffer, position, options));
2010                     position = 0;
2011                 } // end if: encoding
2012                 else {
2013                     throw new java.io.IOException("Base64 input not properly padded.");
2014                 } // end else: decoding
2015             } // end if: buffer partially full
2016 
2017         } // end flush
2018 
2019         /**
2020          * Flushes and closes (I think, in the superclass) the stream.
2021          * 
2022          * @since 1.3
2023          */
2024         @Override
2025         public void close() throws java.io.IOException {
2026             // 1. Ensure that pending characters are written
2027             flushBase64();
2028 
2029             // 2. Actually close the stream
2030             // Base class both flushes and closes.
2031             super.close();
2032 
2033             buffer = null;
2034             out = null;
2035         } // end close
2036 
2037         /**
2038          * Suspends encoding of the stream. May be helpful if you need to embed
2039          * a piece of base64-encoded data in a stream.
2040          * 
2041          * @throws java.io.IOException
2042          *             if there's an error flushing
2043          * @since 1.5.1
2044          */
2045         public void suspendEncoding() throws java.io.IOException {
2046             flushBase64();
2047             this.suspendEncoding = true;
2048         } // end suspendEncoding
2049 
2050         /**
2051          * Resumes encoding of the stream. May be helpful if you need to embed a
2052          * piece of base64-encoded data in a stream.
2053          * 
2054          * @since 1.5.1
2055          */
2056         public void resumeEncoding() {
2057             this.suspendEncoding = false;
2058         } // end resumeEncoding
2059 
2060     } // end inner class OutputStream
2061 
2062 } // end class Base64