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