001: package org.objectweb.celtix.common.util;
002:
003: /**
004: * Base64Utility - this static class provides useful base64
005: * encoding utilities.
006: * <p>
007: * @author Darach Ennis
008: * @author Craig Ryan
009: */
010:
011: // Java imports
012: import java.io.IOException;
013: import java.io.OutputStream;
014: import java.io.Writer;
015: import java.util.logging.Logger;
016:
017: import org.objectweb.celtix.common.i18n.Message;
018: import org.objectweb.celtix.common.logging.LogUtils;
019:
020: /**
021: * This class converts to/from base64. The alternative conversions include:
022: *
023: * encode:
024: * byte[] into String
025: * byte[] into char[]
026: * byte[] into OutStream
027: * byte[] into Writer
028: * decode:
029: * char[] into byte[]
030: * String into byte[]
031: * char[] into OutStream
032: * String into OutStream
033: *
034: */
035: public final class Base64Utility {
036:
037: private static final Logger LOG = LogUtils
038: .getL7dLogger(Base64Utility.class);
039:
040: // base 64 character set
041: //
042: private static final char[] BCS = { 'A', 'B', 'C', 'D', 'E', 'F',
043: 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
044: 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
045: 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
046: 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1',
047: '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
048:
049: // base 64 wadding
050: private static final char PAD = '=';
051:
052: // size of base 64 decode table
053: private static final int BDTSIZE = 128;
054:
055: // base 64 decode table
056: private static final byte[] BDT = new byte[128];
057:
058: private static final int PAD_SIZE0 = 1;
059: private static final int PAD_SIZE4 = 2;
060: private static final int PAD_SIZE8 = 3;
061:
062: // class static intializer for building decode table
063: static {
064: for (int i = 0; i < BDTSIZE; i++) {
065: BDT[i] = Byte.MAX_VALUE;
066: }
067:
068: for (int i = 0; i < BCS.length; i++) {
069: BDT[BCS[i]] = (byte) i;
070: }
071: }
072:
073: private Base64Utility() {
074: //utility class, never constructed
075: }
076:
077: /**
078: * The <code>decode_chunk</code> routine decodes a chunk of data
079: * into its native encoding.
080: *
081: * base64 encodes each 3 octets of data into 4 characters from a
082: * limited 64 character set. The 3 octets are joined to form
083: * 24 bits which are then split into 4 x 6bit values. Each 6 bit
084: * value is then used as an index into the 64 character table of
085: * base64 chars. If the total data length is not a 3 octet multiple
086: * the '=' char is used as padding for the final 4 char group,
087: * either 1 octet + '==' or 2 octets + '='.
088: *
089: * @param id The input data to be processed
090: * @param o The offset from which to begin processing
091: * @param l The length (bound) at which processing is to end
092: * @return The decoded data
093: * @exception Base64Exception Thrown is processing fails due to
094: * formatting exceptions in the encoded data
095: */
096: public static byte[] decodeChunk(char[] id, int o, int l)
097: throws Base64Exception {
098:
099: // Keep it simple - must be >= 4 and a multiple of 4. Unpadded
100: // base64 data contain < 3 octets is invalid.
101: //
102: if (((l - o) % 4 != 0) || (l - o) < 4) {
103: return null;
104: }
105:
106: char[] ib = new char[4];
107: int ibcount = 0;
108:
109: // cryan. Calc the num of octets. Each 4 chars of base64 chars
110: // (representing 24 bits) encodes 3 octets.
111: //
112: int octetCount = 3 * (l / 4);
113:
114: // Final 4 chars may contain 3 octets or padded to contain
115: // 1 or 2 octets.
116: //
117: if (id[l - 1] == PAD) {
118: // TT== means last 4 chars encode 8 bits (ie subtract 2)
119: // TTT= means last 4 chars encode 16 bits (ie subtract 1)
120: octetCount -= (id[l - 2] == PAD) ? 2 : 1;
121: }
122:
123: byte[] ob = new byte[octetCount];
124: int obcount = 0;
125:
126: for (int i = o; i < o + l && i < id.length; i++) {
127: if (id[i] == PAD || id[i] < BDT.length
128: && BDT[id[i]] != Byte.MAX_VALUE) {
129:
130: ib[ibcount++] = id[i];
131:
132: // Decode each 4 char sequence.
133: //
134: if (ibcount == ib.length) {
135: ibcount = 0;
136: obcount += processEncodeme(ib, ob, obcount);
137: }
138: }
139: }
140:
141: return ob;
142: }
143:
144: public static byte[] decode(String id) throws Base64Exception {
145: char[] cd = id.toCharArray();
146: return decodeChunk(cd, 0, cd.length);
147: }
148:
149: public static void decode(char[] id, int o, int l,
150: OutputStream ostream) throws Base64Exception {
151:
152: try {
153: ostream.write(decodeChunk(id, o, l));
154: } catch (IOException e) {
155: // convert exception to Base64Exception
156: throw new Base64Exception(new Message(
157: "BASE64_RUNTIME_EXCEPTION", LOG), e);
158: }
159: }
160:
161: public static void decode(String id, OutputStream ostream)
162: throws Base64Exception {
163:
164: try {
165: char[] cd = id.toCharArray();
166: ostream.write(decodeChunk(cd, 0, cd.length));
167: } catch (IOException e) {
168: throw new Base64Exception(new Message(
169: "BASE64_DECODE_IOEXCEPTION", LOG), e);
170: }
171: }
172:
173: // Returns base64 representation of specified byte array.
174: //
175: public static String encode(byte[] id) {
176: char[] cd = encodeChunk(id, 0, id.length);
177: return new String(cd, 0, cd.length);
178: }
179:
180: // Returns base64 representation of specified byte array.
181: //
182: public static char[] encodeChunk(byte[] id, int o, int l) {
183: if (l <= 0) {
184: return null;
185: }
186:
187: char[] out;
188:
189: // If not a multiple of 3 octets then a final padded 4 char
190: // slot is needed.
191: //
192: if ((l - o) % 3 == 0) {
193: out = new char[l / 3 * 4];
194: } else {
195: out = new char[l / 3 * 4 + 4];
196: }
197:
198: int rindex = o;
199: int windex = 0;
200: int rest = l - o;
201:
202: while (rest >= 3) {
203: int i = ((id[rindex] & 0xff) << 16)
204: + ((id[rindex + 1] & 0xff) << 8)
205: + (id[rindex + 2] & 0xff);
206:
207: out[windex++] = BCS[i >> 18];
208: out[windex++] = BCS[(i >> 12) & 0x3f];
209: out[windex++] = BCS[(i >> 6) & 0x3f];
210: out[windex++] = BCS[i & 0x3f];
211: rindex += 3;
212: rest -= 3;
213: }
214:
215: if (rest == 1) {
216: int i = id[rindex] & 0xff;
217: out[windex++] = BCS[i >> 2];
218: out[windex++] = BCS[(i << 4) & 0x3f];
219: out[windex++] = PAD;
220: out[windex++] = PAD;
221: } else if (rest == 2) {
222: int i = ((id[rindex] & 0xff) << 8)
223: + (id[rindex + 1] & 0xff);
224: out[windex++] = BCS[i >> 10];
225: out[windex++] = BCS[(i >> 4) & 0x3f];
226: out[windex++] = BCS[(i << 2) & 0x3f];
227: out[windex++] = PAD;
228: }
229: return out;
230: }
231:
232: //
233: // Outputs base64 representation of the specified byte array
234: // to a byte stream.
235: //
236: public static void encodeChunk(byte[] id, int o, int l,
237: OutputStream ostream) throws Base64Exception {
238: try {
239: ostream.write(new String(encodeChunk(id, o, l)).getBytes());
240: } catch (IOException e) {
241: throw new Base64Exception(new Message(
242: "BASE64_ENCODE_IOEXCEPTION", LOG), e);
243: }
244: }
245:
246: // Outputs base64 representation of the specified byte
247: // array to a character stream.
248: //
249: public static void encode(byte[] id, int o, int l, Writer writer)
250: throws Base64Exception {
251: try {
252: writer.write(encodeChunk(id, o, l));
253: } catch (IOException e) {
254: throw new Base64Exception(new Message(
255: "BASE64_ENCODE_WRITER_IOEXCEPTION", LOG), e);
256: }
257: }
258:
259: //---- Private static methods --------------------------------------
260:
261: /**
262: * The <code>process</code> routine processes an atomic base64
263: * unit of encoding (encodeme) into its native encoding. This class is
264: * used by decode routines to do the grunt work of decoding
265: * base64 encoded information
266: *
267: * @param ib Input character buffer of encoded bytes
268: * @param ob Output byte buffer of decoded bytes
269: * @param p Pointer to the encodeme of interest
270: * @return The decoded encodeme
271: * @exception Base64Exception Thrown is processing fails due to
272: * formatting exceptions in the encoded data
273: */
274: private static int processEncodeme(char[] ib, byte[] ob, int p)
275: throws Base64Exception {
276:
277: int spad = PAD_SIZE8;
278: if (ib[3] == PAD) {
279: spad = PAD_SIZE4;
280: }
281: if (ib[2] == PAD) {
282: spad = PAD_SIZE0;
283: }
284:
285: int b0 = BDT[ib[0]];
286: int b1 = BDT[ib[1]];
287: int b2 = BDT[ib[2]];
288: int b3 = BDT[ib[3]];
289:
290: switch (spad) {
291: case PAD_SIZE0:
292: ob[p] = (byte) (b0 << 2 & 0xfc | b1 >> 4 & 0x3);
293: return PAD_SIZE0;
294: case PAD_SIZE4:
295: ob[p++] = (byte) (b0 << 2 & 0xfc | b1 >> 4 & 0x3);
296: ob[p] = (byte) (b1 << 4 & 0xf0 | b2 >> 2 & 0xf);
297: return PAD_SIZE4;
298: case PAD_SIZE8:
299: ob[p++] = (byte) (b0 << 2 & 0xfc | b1 >> 4 & 0x3);
300: ob[p++] = (byte) (b1 << 4 & 0xf0 | b2 >> 2 & 0xf);
301: ob[p] = (byte) (b2 << 6 & 0xc0 | b3 & 0x3f);
302: return PAD_SIZE8;
303: default:
304: // We should never get here
305: throw new IllegalStateException();
306: }
307: }
308:
309: }
|