001: /*
002: Copyright (C) 2002-2004 MySQL AB
003:
004: This program is free software; you can redistribute it and/or modify
005: it under the terms of version 2 of the GNU General Public License as
006: published by the Free Software Foundation.
007:
008: There are special exceptions to the terms and conditions of the GPL
009: as it is applied to this software. View the full text of the
010: exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
011: software distribution.
012:
013: This program is distributed in the hope that it will be useful,
014: but WITHOUT ANY WARRANTY; without even the implied warranty of
015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: GNU General Public License for more details.
017:
018: You should have received a copy of the GNU General Public License
019: along with this program; if not, write to the Free Software
020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021:
022:
023:
024: */
025: package com.mysql.jdbc;
026:
027: import java.io.UnsupportedEncodingException;
028:
029: import java.sql.SQLException;
030: import java.util.HashMap;
031: import java.util.Map;
032:
033: /**
034: * Converter for char[]->byte[] and byte[]->char[] for single-byte character
035: * sets.
036: *
037: * Much faster (5-6x) than the built-in solution that ships with the JVM, even
038: * with JDK-1.4.x and NewIo.
039: *
040: * @author Mark Matthews
041: */
042: public class SingleByteCharsetConverter {
043:
044: private static final int BYTE_RANGE = (1 + Byte.MAX_VALUE)
045: - Byte.MIN_VALUE;
046: private static byte[] allBytes = new byte[BYTE_RANGE];
047: private static final Map CONVERTER_MAP = new HashMap();
048:
049: private final static byte[] EMPTY_BYTE_ARRAY = new byte[0];
050:
051: // The initial charToByteMap, with all char mappings mapped
052: // to (byte) '?', so that unknown characters are mapped to '?'
053: // instead of '\0' (which means end-of-string to MySQL).
054: private static byte[] unknownCharsMap = new byte[65536];
055:
056: static {
057: for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
058: allBytes[i - Byte.MIN_VALUE] = (byte) i;
059: }
060:
061: for (int i = 0; i < unknownCharsMap.length; i++) {
062: unknownCharsMap[i] = (byte) '?'; // use something 'sane' for
063: // unknown chars
064: }
065: }
066:
067: // ~ Instance fields
068: // --------------------------------------------------------
069:
070: /**
071: * Get a converter for the given encoding name
072: *
073: * @param encodingName
074: * the Java character encoding name
075: *
076: * @return a converter for the given encoding name
077: * @throws UnsupportedEncodingException
078: * if the character encoding is not supported
079: */
080: public static synchronized SingleByteCharsetConverter getInstance(
081: String encodingName, Connection conn)
082: throws UnsupportedEncodingException, SQLException {
083: SingleByteCharsetConverter instance = (SingleByteCharsetConverter) CONVERTER_MAP
084: .get(encodingName);
085:
086: if (instance == null) {
087: instance = initCharset(encodingName);
088: }
089:
090: return instance;
091: }
092:
093: /**
094: * Initialize the shared instance of a converter for the given character
095: * encoding.
096: *
097: * @param javaEncodingName
098: * the Java name for the character set to initialize
099: * @return a converter for the given character set
100: * @throws UnsupportedEncodingException
101: * if the character encoding is not supported
102: */
103: public static SingleByteCharsetConverter initCharset(
104: String javaEncodingName)
105: throws UnsupportedEncodingException, SQLException {
106: if (CharsetMapping.isMultibyteCharset(javaEncodingName)) {
107: return null;
108: }
109:
110: SingleByteCharsetConverter converter = new SingleByteCharsetConverter(
111: javaEncodingName);
112:
113: CONVERTER_MAP.put(javaEncodingName, converter);
114:
115: return converter;
116: }
117:
118: // ~ Constructors
119: // -----------------------------------------------------------
120:
121: /**
122: * Convert the byte buffer from startPos to a length of length to a string
123: * using the default platform encoding.
124: *
125: * @param buffer
126: * the bytes to convert
127: * @param startPos
128: * the index to start at
129: * @param length
130: * the number of bytes to convert
131: * @return the String representation of the given bytes
132: */
133: public static String toStringDefaultEncoding(byte[] buffer,
134: int startPos, int length) {
135: return new String(buffer, startPos, length);
136: }
137:
138: // ~ Methods
139: // ----------------------------------------------------------------
140:
141: private char[] byteToChars = new char[BYTE_RANGE];
142:
143: private byte[] charToByteMap = new byte[65536];
144:
145: /**
146: * Prevent instantiation, called out of static method initCharset().
147: *
148: * @param encodingName
149: * a JVM character encoding
150: * @throws UnsupportedEncodingException
151: * if the JVM does not support the encoding
152: */
153: private SingleByteCharsetConverter(String encodingName)
154: throws UnsupportedEncodingException {
155: String allBytesString = new String(allBytes, 0, BYTE_RANGE,
156: encodingName);
157: int allBytesLen = allBytesString.length();
158:
159: System.arraycopy(unknownCharsMap, 0, this .charToByteMap, 0,
160: this .charToByteMap.length);
161:
162: for (int i = 0; i < BYTE_RANGE && i < allBytesLen; i++) {
163: char c = allBytesString.charAt(i);
164: this .byteToChars[i] = c;
165: this .charToByteMap[c] = allBytes[i];
166: }
167: }
168:
169: public final byte[] toBytes(char[] c) {
170: if (c == null) {
171: return null;
172: }
173:
174: int length = c.length;
175: byte[] bytes = new byte[length];
176:
177: for (int i = 0; i < length; i++) {
178: bytes[i] = this .charToByteMap[c[i]];
179: }
180:
181: return bytes;
182: }
183:
184: public final byte[] toBytesWrapped(char[] c, char beginWrap,
185: char endWrap) {
186: if (c == null) {
187: return null;
188: }
189:
190: int length = c.length + 2;
191: int charLength = c.length;
192:
193: byte[] bytes = new byte[length];
194: bytes[0] = this .charToByteMap[beginWrap];
195:
196: for (int i = 0; i < charLength; i++) {
197: bytes[i + 1] = this .charToByteMap[c[i]];
198: }
199:
200: bytes[length - 1] = this .charToByteMap[endWrap];
201:
202: return bytes;
203: }
204:
205: public final byte[] toBytes(char[] chars, int offset, int length) {
206: if (chars == null) {
207: return null;
208: }
209:
210: if (length == 0) {
211: return EMPTY_BYTE_ARRAY;
212: }
213:
214: byte[] bytes = new byte[length];
215:
216: for (int i = 0; (i < length); i++) {
217: bytes[i] = this .charToByteMap[chars[i + offset]];
218: }
219:
220: return bytes;
221: }
222:
223: /**
224: * Convert the given string to an array of bytes.
225: *
226: * @param s
227: * the String to convert
228: * @return the bytes that make up the String
229: */
230: public final byte[] toBytes(String s) {
231: if (s == null) {
232: return null;
233: }
234:
235: int length = s.length();
236: byte[] bytes = new byte[length];
237:
238: for (int i = 0; i < length; i++) {
239: bytes[i] = this .charToByteMap[s.charAt(i)];
240: }
241:
242: return bytes;
243: }
244:
245: public final byte[] toBytesWrapped(String s, char beginWrap,
246: char endWrap) {
247: if (s == null) {
248: return null;
249: }
250:
251: int stringLength = s.length();
252:
253: int length = stringLength + 2;
254:
255: byte[] bytes = new byte[length];
256:
257: bytes[0] = this .charToByteMap[beginWrap];
258:
259: for (int i = 0; i < stringLength; i++) {
260: bytes[i + 1] = this .charToByteMap[s.charAt(i)];
261: }
262:
263: bytes[length - 1] = this .charToByteMap[endWrap];
264:
265: return bytes;
266: }
267:
268: /**
269: * Convert the given string to an array of bytes.
270: *
271: * @param s
272: * the String to convert
273: * @param offset
274: * the offset to start at
275: * @param length
276: * length (max) to convert
277: *
278: * @return the bytes that make up the String
279: */
280: public final byte[] toBytes(String s, int offset, int length) {
281: if (s == null) {
282: return null;
283: }
284:
285: if (length == 0) {
286: return EMPTY_BYTE_ARRAY;
287: }
288:
289: byte[] bytes = new byte[length];
290:
291: for (int i = 0; (i < length); i++) {
292: char c = s.charAt(i + offset);
293: bytes[i] = this .charToByteMap[c];
294: }
295:
296: return bytes;
297: }
298:
299: /**
300: * Convert the byte buffer to a string using this instance's character
301: * encoding.
302: *
303: * @param buffer
304: * the bytes to convert to a String
305: * @return the converted String
306: */
307: public final String toString(byte[] buffer) {
308: return toString(buffer, 0, buffer.length);
309: }
310:
311: /**
312: * Convert the byte buffer from startPos to a length of length to a string
313: * using this instance's character encoding.
314: *
315: * @param buffer
316: * the bytes to convert
317: * @param startPos
318: * the index to start at
319: * @param length
320: * the number of bytes to convert
321: * @return the String representation of the given bytes
322: */
323: public final String toString(byte[] buffer, int startPos, int length) {
324: char[] charArray = new char[length];
325: int readpoint = startPos;
326:
327: for (int i = 0; i < length; i++) {
328: charArray[i] = this .byteToChars[buffer[readpoint]
329: - Byte.MIN_VALUE];
330: readpoint++;
331: }
332:
333: return new String(charArray);
334: }
335: }
|