001: // jTDS JDBC Driver for Microsoft SQL Server and Sybase
002: // Copyright (C) 2004 The jTDS Project
003: //
004: // This library is free software; you can redistribute it and/or
005: // modify it under the terms of the GNU Lesser General Public
006: // License as published by the Free Software Foundation; either
007: // version 2.1 of the License, or (at your option) any later version.
008: //
009: // This library is distributed in the hope that it will be useful,
010: // but WITHOUT ANY WARRANTY; without even the implied warranty of
011: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: // Lesser General Public License for more details.
013: //
014: // You should have received a copy of the GNU Lesser General Public
015: // License along with this library; if not, write to the Free Software
016: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: //
018: package net.sourceforge.jtds.jdbc;
019:
020: import java.sql.SQLException;
021: import java.util.HashMap;
022: import java.util.Properties;
023: import java.util.Enumeration;
024: import java.io.InputStream;
025: import java.io.IOException;
026:
027: import net.sourceforge.jtds.util.Logger;
028:
029: /**
030: * Loads and stores information about character sets. Static fields and methods
031: * are concerned with loading, storing and retrieval of all character set
032: * information, while non-static fields and methods describe a particular
033: * character set (Java charset name and whether it's a multi-byte charset).
034: * <p>
035: * <b>Note:</b> Only one <code>CharsetInfo</code> instance exists per charset.
036: * This allows simple equality comparisons between instances retrieved with any
037: * of the <code>get</code> methods.
038: *
039: * @author Alin Sinpalean
040: * @version $Id: CharsetInfo.java,v 1.5 2007/07/08 17:28:23 bheineman Exp $
041: */
042: public final class CharsetInfo {
043: //
044: // Static fields and methods
045: //
046:
047: /** Name of the <code>Charsets.properties</code> resource. */
048: private static final String CHARSETS_RESOURCE_NAME = "net/sourceforge/jtds/jdbc/Charsets.properties";
049:
050: /** Server charset to Java charset map. */
051: private static final HashMap charsets = new HashMap();
052:
053: /** Locale id to Java charset map. */
054: private static final HashMap lcidToCharsetMap = new HashMap();
055:
056: /** Sort order to Java charset map. */
057: private static final CharsetInfo[] sortToCharsetMap = new CharsetInfo[256];
058:
059: static {
060: // Load character set mappings
061: try {
062: InputStream stream = null;
063: // getContextClassLoader needed to ensure driver
064: // works with Tomcat class loading rules.
065: ClassLoader classLoader = Thread.currentThread()
066: .getContextClassLoader();
067:
068: if (classLoader != null) {
069: stream = classLoader
070: .getResourceAsStream(CHARSETS_RESOURCE_NAME);
071: }
072:
073: if (stream == null) {
074: // The doPrivileged() call stops the SecurityManager from
075: // checking further in the stack trace whether all callers have
076: // the permission to load Charsets.properties
077: stream = (InputStream) java.security.AccessController
078: .doPrivileged(new java.security.PrivilegedAction() {
079: public Object run() {
080: ClassLoader loader = CharsetInfo.class
081: .getClassLoader();
082: // getClassLoader() may return null if the class was loaded by
083: // the bootstrap ClassLoader
084: if (loader == null) {
085: loader = ClassLoader
086: .getSystemClassLoader();
087: }
088:
089: return loader
090: .getResourceAsStream(CHARSETS_RESOURCE_NAME);
091: }
092: });
093: }
094:
095: if (stream != null) {
096: Properties tmp = new Properties();
097: tmp.load(stream);
098:
099: HashMap instances = new HashMap();
100:
101: for (Enumeration e = tmp.propertyNames(); e
102: .hasMoreElements();) {
103: String key = (String) e.nextElement();
104: CharsetInfo value = new CharsetInfo(tmp
105: .getProperty(key));
106:
107: // Ensure only one CharsetInfo instance exists per charset
108: CharsetInfo prevInstance = (CharsetInfo) instances
109: .get(value.getCharset());
110: if (prevInstance != null) {
111: if (prevInstance.isWideChars() != value
112: .isWideChars()) {
113: throw new IllegalStateException(
114: "Inconsistent Charsets.properties");
115: }
116: value = prevInstance;
117: }
118:
119: if (key.startsWith("LCID_")) {
120: Integer lcid = new Integer(key.substring(5));
121: lcidToCharsetMap.put(lcid, value);
122: } else if (key.startsWith("SORT_")) {
123: sortToCharsetMap[Integer.parseInt(key
124: .substring(5))] = value;
125: } else {
126: charsets.put(key, value);
127: }
128: }
129: } else {
130: Logger.println("Can't load Charsets.properties");
131: }
132: } catch (IOException e) {
133: // Can't load properties file for some reason
134: Logger.logException(e);
135: }
136: }
137:
138: /**
139: * Retrieves the <code>CharsetInfo</code> instance asociated with the
140: * specified server charset.
141: *
142: * @param serverCharset the server-specific character set name
143: * @return the associated <code>CharsetInfo</code>
144: */
145: public static CharsetInfo getCharset(String serverCharset) {
146: return (CharsetInfo) charsets.get(serverCharset.toUpperCase());
147: }
148:
149: /**
150: * Retrieves the <code>CharsetInfo</code> instance asociated with the
151: * specified LCID.
152: *
153: * @param lcid the server LCID
154: * @return the associated <code>CharsetInfo</code>
155: */
156: public static CharsetInfo getCharsetForLCID(int lcid) {
157: return (CharsetInfo) lcidToCharsetMap.get(new Integer(lcid));
158: }
159:
160: /**
161: * Retrieves the <code>CharsetInfo</code> instance asociated with the
162: * specified sort order.
163: *
164: * @param sortOrder the server sort order
165: * @return the associated <code>CharsetInfo</code>
166: */
167: public static CharsetInfo getCharsetForSortOrder(int sortOrder) {
168: return sortToCharsetMap[sortOrder];
169: }
170:
171: /**
172: * Retrieves the <code>CharsetInfo</code> instance asociated with the
173: * specified collation.
174: *
175: * @param collation the server LCID
176: * @return the associated <code>CharsetInfo</code>
177: */
178: public static CharsetInfo getCharset(byte[] collation)
179: throws SQLException {
180: CharsetInfo charset;
181:
182: if (collation[4] != 0) {
183: // The charset is determined by the sort order
184: charset = getCharsetForSortOrder((int) collation[4] & 0xFF);
185: } else {
186: // The charset is determined by the LCID
187: charset = getCharsetForLCID(((int) collation[2] & 0x0F) << 16
188: | ((int) collation[1] & 0xFF) << 8
189: | ((int) collation[0] & 0xFF));
190: }
191:
192: if (charset == null) {
193: throw new SQLException(Messages.get(
194: "error.charset.nocollation", Support
195: .toHex(collation)), "2C000");
196: }
197:
198: return charset;
199: }
200:
201: //
202: // Non-static fields and methods
203: //
204:
205: /** The Java character set name. */
206: private final String charset;
207: /** Indicates whether current charset is wide (ie multi-byte). */
208: private final boolean wideChars;
209:
210: /**
211: * Constructs a <code>CharsetInfo</code> object from a character set
212: * descriptor of the form: charset preceded by a numeric value indicating
213: * whether it's a multibyte character set (>1) or not (1) and a vertical
214: * bar (|), eg "1|Cp1252" or "2|MS936".
215: *
216: * @param descriptor the charset descriptor
217: */
218: public CharsetInfo(String descriptor) {
219: wideChars = !"1".equals(descriptor.substring(0, 1));
220: charset = descriptor.substring(2);
221: }
222:
223: /**
224: * Retrieves the charset name.
225: */
226: public String getCharset() {
227: return charset;
228: }
229:
230: /**
231: * Retrieves whether the caracter set is wide (ie multi-byte).
232: */
233: public boolean isWideChars() {
234: return wideChars;
235: }
236:
237: public boolean equals(Object o) {
238: if (this == o) {
239: return true;
240: }
241: if (!(o instanceof CharsetInfo)) {
242: return false;
243: }
244:
245: final CharsetInfo charsetInfo = (CharsetInfo) o;
246: if (!charset.equals(charsetInfo.charset)) {
247: return false;
248: }
249:
250: return true;
251: }
252:
253: public int hashCode() {
254: return charset.hashCode();
255: }
256:
257: public String toString() {
258: return charset;
259: }
260: }
|