1417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughespackage SQLite;
2417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
3417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes/**
4417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * String encoder/decoder for SQLite.
5417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes *
6417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * This module was kindly donated by Eric van der Maarel of Nedap N.V.
7417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes *
8417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * This encoder was implemented based on an original idea from an anonymous
9417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * author in the source code of the SQLite distribution.
10417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * I feel obliged to provide a quote from the original C-source code:
11417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes *
12417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * "The author disclaims copyright to this source code.  In place of
13417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes *  a legal notice, here is a blessing:
14417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes *
15417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes *     May you do good and not evil.
16417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes *     May you find forgiveness for yourself and forgive others.
17417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes *     May you share freely, never taking more than you give."
18417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes *
19417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes */
20417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
21417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughespublic class StringEncoder {
22417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
23417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    /**
24417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * Encodes the given byte array into a string that can be used by
25417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * the SQLite database. The database cannot handle null (0x00) and
26417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * the character '\'' (0x27). The encoding consists of escaping
27417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * these characters with a reserved character (0x01). The escaping
28417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * is applied after determining and applying a shift that minimizes
29417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * the number of escapes required.
30417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * With this encoding the data of original size n is increased to a
31417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * maximum of 1+(n*257)/254.
32417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * For sufficiently large n the overhead is thus less than 1.2%.
33417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @param a the byte array to be encoded. A null reference is handled as
34417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     *     an empty array.
35417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @return the encoded bytes as a string. When an empty array is
36417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     *     provided a string of length 1 is returned, the value of
37417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     *     which is bogus.
38417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     *     When decoded with this class' <code>decode</code> method
39417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     *     a string of size 1 will return an empty byte array.
40417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     */
41417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
42417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    public static String encode(byte[] a) {
437a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	// check input
447a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	if (a == null || a.length == 0) {
457a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    // bogus shift, no data
467a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    return "x";
477a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
487a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	// determine count
497a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	int[] cnt = new int[256];
507a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	for (int i = 0 ; i < a.length; i++) {
517a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    cnt[a[i] & 0xff]++;
527a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
537a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	// determine shift for minimum number of escapes
547a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	int shift = 1;
557a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	int nEscapes = a.length;
567a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	for (int i = 1; i < 256; i++) {
577a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    if (i == '\'') {
587a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		continue;
597a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    }
607a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    int sum = cnt[i] + cnt[(i + 1) & 0xff] + cnt[(i + '\'') & 0xff];
617a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    if (sum < nEscapes) {
627a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		nEscapes = sum;
637a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		shift = i;
647a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		if (nEscapes == 0) {
657a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		    // cannot become smaller
667a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		    break;
677a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		}
687a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    }
697a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
707a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	// construct encoded output
717a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	int outLen = a.length + nEscapes + 1;
727a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	StringBuffer out = new StringBuffer(outLen);
737a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	out.append((char)shift);
747a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	for (int i = 0; i < a.length; i++) {
757a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    // apply shift
767a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    char c = (char)((a[i] - shift)&0xff);
777a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    // insert escapes
787a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    if (c == 0) { // forbidden
797a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		out.append((char)1);
807a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		out.append((char)1);
817a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    } else if (c == 1) { // escape character
827a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		out.append((char)1);
837a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		out.append((char)2);
847a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    } else if (c == '\'') { // forbidden
857a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		out.append((char)1);
867a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		out.append((char)3);
877a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    } else {
887a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		out.append(c);
897a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    }
907a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
917a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	return out.toString();
92417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    }
93417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
94417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    /**
95417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * Decodes the given string that is assumed to be a valid encoding
96417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * of a byte array. Typically the given string is generated by
97417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * this class' <code>encode</code> method.
98417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @param s the given string encoding.
99417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @return the byte array obtained from the decoding.
100417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @throws IllegalArgumentException when the string given is not
101417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     *    a valid encoded string for this encoder.
102417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     */
103417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
104417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    public static byte[] decode(String s) {
1057a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	char[] a = s.toCharArray();
1067a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	if (a.length > 2 && a[0] == 'X' &&
1077a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    a[1] == '\'' && a[a.length-1] == '\'') {
1087a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    // SQLite3 BLOB syntax
1097a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    byte[] result = new byte[(a.length-3)/2];
1107a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    for (int i = 2, k = 0; i < a.length - 1; i += 2, k++) {
1117a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		byte tmp;
1127a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		switch (a[i]) {
1137a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '0': tmp = 0; break;
1147a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '1': tmp = 1; break;
1157a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '2': tmp = 2; break;
1167a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '3': tmp = 3; break;
1177a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '4': tmp = 4; break;
1187a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '5': tmp = 5; break;
1197a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '6': tmp = 6; break;
1207a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '7': tmp = 7; break;
1217a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '8': tmp = 8; break;
1227a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '9': tmp = 9; break;
1237a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'A':
1247a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'a': tmp = 10; break;
1257a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'B':
1267a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'b': tmp = 11; break;
1277a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'C':
1287a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'c': tmp = 12; break;
1297a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'D':
1307a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'd': tmp = 13; break;
1317a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'E':
1327a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'e': tmp = 14; break;
1337a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'F':
1347a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'f': tmp = 15; break;
1357a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		default:  tmp = 0; break;
1367a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		}
1377a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		result[k] = (byte) (tmp << 4);
1387a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		switch (a[i+1]) {
1397a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '0': tmp = 0; break;
1407a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '1': tmp = 1; break;
1417a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '2': tmp = 2; break;
1427a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '3': tmp = 3; break;
1437a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '4': tmp = 4; break;
1447a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '5': tmp = 5; break;
1457a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '6': tmp = 6; break;
1467a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '7': tmp = 7; break;
1477a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '8': tmp = 8; break;
1487a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case '9': tmp = 9; break;
1497a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'A':
1507a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'a': tmp = 10; break;
1517a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'B':
1527a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'b': tmp = 11; break;
1537a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'C':
1547a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'c': tmp = 12; break;
1557a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'D':
1567a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'd': tmp = 13; break;
1577a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'E':
1587a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'e': tmp = 14; break;
1597a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'F':
1607a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		case 'f': tmp = 15; break;
1617a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		default:  tmp = 0; break;
1627a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		}
1637a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		result[k] |= tmp;
1647a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    }
1657a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    return result;
1667a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
1677a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	// first element is the shift
1687a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	byte[] result = new byte[a.length-1];
1697a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	int i = 0;
1707a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	int shift = s.charAt(i++);
1717a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	int j = 0;
1727a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	while (i < s.length()) {
1737a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    int c;
1747a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    if ((c = s.charAt(i++)) == 1) { // escape character found
1757a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		if ((c = s.charAt(i++)) == 1) {
1767a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		    c = 0;
1777a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		} else if (c == 2) {
1787a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		    c = 1;
1797a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		} else if (c == 3) {
1807a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		    c = '\'';
1817a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		} else {
1827a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		    throw new IllegalArgumentException(
1837a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes			"invalid string passed to decoder: " + j);
1847a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes		}
1857a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    }
1867a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    // do shift
1877a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    result[j++] = (byte)((c + shift) & 0xff);
1887a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
1897a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	int outLen = j;
1907a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	// provide array of correct length
1917a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	if (result.length != outLen) {
1927a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    result = byteCopy(result, 0, outLen, new byte[outLen]);
1937a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
1947a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	return result;
195417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    }
196417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
197417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    /**
198417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * Copies count elements from source, starting at element with
199417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * index offset, to the given target.
200417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @param source the source.
201417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @param offset the offset.
202417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @param count the number of elements to be copied.
203417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @param target the target to be returned.
204417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @return the target being copied to.
205417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     */
206417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
207417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    private static byte[] byteCopy(byte[] source, int offset,
2087a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes				   int count, byte[] target) {
2097a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	for (int i = offset, j = 0; i < offset + count; i++, j++) {
2107a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    target[j] = source[i];
2117a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
2127a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	return target;
213417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    }
214417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
215417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
216417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    static final char[] xdigits = {
2177a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	'0', '1', '2', '3', '4', '5', '6', '7',
2187a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
219417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    };
220417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
221417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    /**
222417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * Encodes the given byte array into SQLite3 blob notation, ie X'..'
223417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @param a the byte array to be encoded. A null reference is handled as
224417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     *     an empty array.
225417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     * @return the encoded bytes as a string.
226417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes     */
227417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes
228417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    public static String encodeX(byte[] a) {
2297a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	// check input
2307a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	if (a == null || a.length == 0) {
2317a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    return "X''";
2327a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
2337a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	int outLen = a.length * 2 + 3;
2347a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	StringBuffer out = new StringBuffer(outLen);
2357a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	out.append('X');
2367a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	out.append('\'');
2377a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	for (int i = 0; i < a.length; i++) {
2387a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    out.append(xdigits[(a[i] >> 4) & 0x0F]);
2397a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	    out.append(xdigits[a[i] & 0x0F]);
2407a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	}
2417a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	out.append('\'');
2427a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes	return out.toString();
243417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes    }
244417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes}
245