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