001: /*
002: * @(#)Converters.java 1.16 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.io;
029:
030: import java.io.UnsupportedEncodingException;
031: import java.lang.ref.SoftReference;
032: import java.util.Properties;
033:
034: /**
035: * Package-private utility class that caches the default converter classes and
036: * provides other logic common to both the ByteToCharConverter and
037: * CharToByteConverter classes.
038: *
039: * @author Mark Reinhold
040: * @version 1.7, 00/07/07
041: * @since JDK1.2
042: */
043:
044: public class Converters {
045:
046: private Converters() {
047: } /* To prevent instantiation */
048:
049: /* Lock for all static fields in this class */
050: private static Object lock = Converters.class;
051:
052: /* Cached values of system properties */
053: private static String converterPackageName = null; /* file.encoding.pkg */
054: private static String defaultEncoding = null; /* file.encoding */
055:
056: /* Converter type constants and names */
057: public static final int BYTE_TO_CHAR = 0;
058: public static final int CHAR_TO_BYTE = 1;
059: private static final String[] converterPrefix = { "ByteToChar",
060: "CharToByte" };
061:
062: private static final int CACHE_SIZE = 3;
063:
064: /* For the default charset, whatever it turns out to be */
065: private static final Object DEFAULT_NAME = new Object();
066:
067: /* Cached converter classes, CACHE_SIZE per converter type. Each cache
068: * entry is a soft reference to a two-object array; the first element of
069: * the array is the converter class, the second is an object (typically a
070: * string) representing the encoding name that was used to request the
071: * converter, e.g.,
072: *
073: * ((Object[])classCache[CHAR_TO_BYTE][i].get())[0]
074: *
075: * will be a CharToByteConverter and
076: *
077: * ((Object[])classCache[CHAR_TO_BYTE][i].get())[1]
078: *
079: * will be the string encoding name used to request it, assuming that cache
080: * entry i is valid.
081: *
082: * Ordinarily we'd do this with a private static utility class, but since
083: * this code can be involved in the startup sequence it's important to keep
084: * the footprint down.
085: */
086: private static SoftReference[][] classCache = new SoftReference[][] {
087: new SoftReference[CACHE_SIZE],
088: new SoftReference[CACHE_SIZE] };
089:
090: private static void moveToFront(Object[] oa, int i) {
091: Object ob = oa[i];
092: for (int j = i; j > 0; j--)
093: oa[j] = oa[j - 1];
094: oa[0] = ob;
095: }
096:
097: private static Class cache(int type, Object encoding) {
098: SoftReference[] srs = classCache[type];
099: for (int i = 0; i < CACHE_SIZE; i++) {
100: SoftReference sr = srs[i];
101: if (sr == null)
102: continue;
103: Object[] oa = (Object[]) sr.get();
104: if (oa == null) {
105: srs[i] = null;
106: continue;
107: }
108: if (oa[1].equals(encoding)) {
109: moveToFront(srs, i);
110: return (Class) oa[0];
111: }
112: }
113: return null;
114: }
115:
116: private static Class cache(int type, Object encoding, Class c) {
117: SoftReference[] srs = classCache[type];
118: srs[CACHE_SIZE - 1] = new SoftReference(new Object[] { c,
119: encoding });
120: moveToFront(srs, CACHE_SIZE - 1);
121: return c;
122: }
123:
124: /* Used to avoid doing expensive charset lookups for charsets that are not
125: * yet directly supported by NIO.
126: */
127: public static boolean isCached(int type, String encoding) {
128: synchronized (lock) {
129: SoftReference[] srs = classCache[type];
130: for (int i = 0; i < CACHE_SIZE; i++) {
131: SoftReference sr = srs[i];
132: if (sr == null)
133: continue;
134: Object[] oa = (Object[]) sr.get();
135: if (oa == null) {
136: srs[i] = null;
137: continue;
138: }
139: if (oa[1].equals(encoding))
140: return true;
141: }
142: return false;
143: }
144: }
145:
146: /** Get the name of the converter package */
147: private static String getConverterPackageName() {
148: String cp = converterPackageName;
149: if (cp != null)
150: return cp;
151: java.security.PrivilegedAction pa = new sun.security.action.GetPropertyAction(
152: "file.encoding.pkg");
153: cp = (String) java.security.AccessController.doPrivileged(pa);
154: if (cp != null) {
155: /* Property is set, so take it as the true converter package */
156: converterPackageName = cp;
157: } else {
158: /* Fall back to sun.io */
159: cp = "sun.io";
160: }
161: return cp;
162: }
163:
164: public static String getDefaultEncodingName() {
165: synchronized (lock) {
166: if (defaultEncoding == null) {
167: java.security.PrivilegedAction pa = new sun.security.action.GetPropertyAction(
168: "file.encoding");
169: defaultEncoding = (String) java.security.AccessController
170: .doPrivileged(pa);
171: }
172: }
173: return defaultEncoding;
174: }
175:
176: public static void resetDefaultEncodingName() {
177: // This method should only be called during VM initialization.
178: synchronized (lock) {
179: defaultEncoding = "ISO-8859-1";
180: Properties p = System.getProperties();
181: p.setProperty("file.encoding", defaultEncoding);
182: System.setProperties(p);
183: }
184: }
185:
186: /**
187: * Get the class that implements the given type of converter for the named
188: * encoding, or throw an UnsupportedEncodingException if no such class can
189: * be found
190: */
191: private static Class getConverterClass(int type, String encoding)
192: throws UnsupportedEncodingException {
193: String enc = null;
194:
195: /* "ISO8859_1" is the canonical name for the ISO-Latin-1 encoding.
196: Native code in the JDK commonly uses the alias "8859_1" instead of
197: "ISO8859_1". We hardwire this alias here in order to avoid loading
198: the full alias table just for this case. */
199: if (!encoding.equals("ISO8859_1")) {
200: if (encoding.equals("8859_1")) {
201: enc = "ISO8859_1";
202: /*
203: * On Solaris with nl_langinfo() called in GetJavaProperties():
204: *
205: * locale undefined -> NULL -> hardcoded default
206: * "C" locale -> "" -> hardcoded default (on 2.6)
207: * "C" locale -> "646" (on 2.7)
208: * "en_US" locale -> "ISO8859-1"
209: * "en_GB" locale -> "ISO8859-1" (on 2.7)
210: * "en_UK" locale -> "ISO8859-1" (on 2.6)
211: */
212: } else if (encoding.equals("ISO8859-1")) {
213: enc = "ISO8859_1";
214: } else if (encoding.equals("646")) {
215: enc = "ASCII";
216: } else {
217: enc = CharacterEncoding.aliasName(encoding);
218: }
219: }
220: if (enc == null) {
221: enc = encoding;
222: }
223:
224: try {
225: return Class.forName(getConverterPackageName() + "."
226: + converterPrefix[type] + enc);
227: } catch (ClassNotFoundException e) {
228: throw new UnsupportedEncodingException(enc);
229: }
230:
231: }
232:
233: /**
234: * Instantiate the given converter class, or throw an
235: * UnsupportedEncodingException if it cannot be instantiated
236: */
237: private static Object newConverter(String enc, Class c)
238: throws UnsupportedEncodingException {
239: try {
240: return c.newInstance();
241: } catch (InstantiationException e) {
242: throw new UnsupportedEncodingException(enc);
243: } catch (IllegalAccessException e) {
244: throw new UnsupportedEncodingException(enc);
245: }
246: }
247:
248: /**
249: * Create a converter object that implements the given type of converter
250: * for the given encoding, or throw an UnsupportedEncodingException if no
251: * appropriate converter class can be found and instantiated
252: */
253: static Object newConverter(int type, String enc)
254: throws UnsupportedEncodingException {
255: Class c;
256: synchronized (lock) {
257: c = cache(type, enc);
258: if (c == null) {
259: c = getConverterClass(type, enc);
260: cache(type, enc, c);
261: }
262: }
263: return newConverter(enc, c);
264: }
265:
266: /**
267: * Find the class that implements the given type of converter for the
268: * default encoding. If the default encoding cannot be determined or is
269: * not yet defined, return a class that implements the fallback default
270: * encoding, which is just ISO 8859-1.
271: */
272: private static Class getDefaultConverterClass(int type) {
273: boolean fillCache = false;
274: Class c;
275:
276: /* First check the class cache */
277: c = cache(type, DEFAULT_NAME);
278: if (c != null)
279: return c;
280:
281: /* Determine the encoding name */
282: String enc = getDefaultEncodingName();
283: if (enc != null) {
284: /* file.encoding has been set, so cache the converter class */
285: fillCache = true;
286: } else {
287: /* file.encoding has not been set, so use a default encoding which
288: will not be cached */
289: enc = "ISO8859_1";
290: }
291:
292: /* We have an encoding name; try to find its class */
293: try {
294: c = getConverterClass(type, enc);
295: if (fillCache) {
296: cache(type, DEFAULT_NAME, c);
297: }
298: } catch (UnsupportedEncodingException x) {
299: /* Can't find the default class, so fall back to ISO 8859-1 */
300: try {
301: c = getConverterClass(type, "ISO8859_1");
302: } catch (UnsupportedEncodingException y) {
303: throw new InternalError("Cannot find default "
304: + converterPrefix[type] + " converter class");
305: }
306: }
307: return c;
308:
309: }
310:
311: /**
312: * Create a converter object that implements the given type of converter
313: * for the default encoding, falling back to ISO 8859-1 if the default
314: * encoding cannot be determined.
315: */
316: static Object newDefaultConverter(int type) {
317: Class c;
318: synchronized (lock) {
319: c = getDefaultConverterClass(type);
320: }
321: try {
322: return newConverter("", c);
323: } catch (UnsupportedEncodingException x) {
324: throw new InternalError(
325: "Cannot instantiate default converter" + " class "
326: + c.getName());
327: }
328: }
329:
330: }
|