1 /*
   2  * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.io;
  27 
  28 import java.io.UnsupportedEncodingException;
  29 import java.lang.ref.SoftReference;
  30 import java.util.Properties;
  31 
  32 /**
  33  * Package-private utility class that caches the default converter classes and
  34  * provides other logic common to both the ByteToCharConverter and
  35  * CharToByteConverter classes.
  36  *
  37  * @author   Mark Reinhold
  38  * @since    1.2
  39  *
  40  * @deprecated Replaced by {@link java.nio.charset}.  THIS API WILL BE
  41  * REMOVED IN J2SE 1.6.
  42  */
  43 @Deprecated
  44 public class Converters {
  45 
  46     private Converters() { }    /* To prevent instantiation */
  47 
  48     /* Lock for all static fields in this class */
  49     private static Object lock = Converters.class;
  50 
  51     /* Cached values of system properties */
  52     private static String converterPackageName = null;  /* file.encoding.pkg */
  53     private static String defaultEncoding = null;       /* file.encoding */
  54 
  55     /* Converter type constants and names */
  56     public static final int BYTE_TO_CHAR = 0;
  57     public static final int CHAR_TO_BYTE = 1;
  58     private static final String[] converterPrefix = { "ByteToChar",
  59                                                       "CharToByte" };
  60 
  61 
  62     // -- Converter class cache --
  63 
  64     private static final int CACHE_SIZE = 3;
  65 
  66     /* For the default charset, whatever it turns out to be */
  67     private static final Object DEFAULT_NAME = new Object();
  68 
  69     /* Cached converter classes, CACHE_SIZE per converter type.  Each cache
  70      * entry is a soft reference to a two-object array; the first element of
  71      * the array is the converter class, the second is an object (typically a
  72      * string) representing the encoding name that was used to request the
  73      * converter, e.g.,
  74      *
  75      *     ((Object[])classCache[CHAR_TO_BYTE][i].get())[0]
  76      *
  77      * will be a CharToByteConverter and
  78      *
  79      *     ((Object[])classCache[CHAR_TO_BYTE][i].get())[1]
  80      *
  81      * will be the string encoding name used to request it, assuming that cache
  82      * entry i is valid.
  83      *
  84      * Ordinarily we'd do this with a private static utility class, but since
  85      * this code can be involved in the startup sequence it's important to keep
  86      * the footprint down.
  87      */
  88     @SuppressWarnings("unchecked")
  89     private static SoftReference<Object[]>[][] classCache
  90         = (SoftReference<Object[]>[][]) new SoftReference<?>[][] {
  91             new SoftReference<?>[CACHE_SIZE],
  92             new SoftReference<?>[CACHE_SIZE]
  93         };
  94 
  95     private static void moveToFront(Object[] oa, int i) {
  96         Object ob = oa[i];
  97         for (int j = i; j > 0; j--)
  98             oa[j] = oa[j - 1];
  99         oa[0] = ob;
 100     }
 101 
 102     private static Class<?> cache(int type, Object encoding) {
 103         SoftReference<Object[]>[] srs = classCache[type];
 104         for (int i = 0; i < CACHE_SIZE; i++) {
 105             SoftReference<Object[]> sr = srs[i];
 106             if (sr == null)
 107                 continue;
 108             Object[] oa = sr.get();
 109             if (oa == null) {
 110                 srs[i] = null;
 111                 continue;
 112             }
 113             if (oa[1].equals(encoding)) {
 114                 moveToFront(srs, i);
 115                 return (Class<?>)oa[0];
 116             }
 117         }
 118         return null;
 119     }
 120 
 121     private static Class<?> cache(int type, Object encoding, Class<?> c) {
 122         SoftReference<Object[]>[] srs = classCache[type];
 123         srs[CACHE_SIZE - 1] = new SoftReference<>(new Object[] { c, encoding });
 124         moveToFront(srs, CACHE_SIZE - 1);
 125         return c;
 126     }
 127 
 128     /* Used to avoid doing expensive charset lookups for charsets that are not
 129      * yet directly supported by NIO.
 130      */
 131     public static boolean isCached(int type, String encoding) {
 132         synchronized (lock) {
 133             SoftReference<Object[]>[] srs = classCache[type];
 134             for (int i = 0; i < CACHE_SIZE; i++) {
 135                 SoftReference<Object[]> sr = srs[i];
 136                 if (sr == null)
 137                     continue;
 138                 Object[] oa = sr.get();
 139                 if (oa == null) {
 140                     srs[i] = null;
 141                     continue;
 142                 }
 143                 if (oa[1].equals(encoding))
 144                     return true;
 145             }
 146             return false;
 147         }
 148     }
 149 
 150 
 151 
 152     /** Get the name of the converter package */
 153     private static String getConverterPackageName() {
 154         String cp = converterPackageName;
 155         if (cp != null) return cp;
 156         java.security.PrivilegedAction<String> pa =
 157             new sun.security.action.GetPropertyAction("file.encoding.pkg");
 158         cp = java.security.AccessController.doPrivileged(pa);
 159         if (cp != null) {
 160             /* Property is set, so take it as the true converter package */
 161             converterPackageName = cp;
 162         } else {
 163             /* Fall back to sun.io */
 164             cp = "sun.io";
 165         }
 166         return cp;
 167     }
 168 
 169     public static String getDefaultEncodingName() {
 170         synchronized (lock) {
 171             if (defaultEncoding == null) {
 172                 java.security.PrivilegedAction<String> pa =
 173                     new sun.security.action.GetPropertyAction("file.encoding");
 174                 defaultEncoding = java.security.AccessController.doPrivileged(pa);
 175             }
 176         }
 177         return defaultEncoding;
 178     }
 179 
 180     public static void resetDefaultEncodingName() {
 181         // This method should only be called during VM initialization.
 182         if (sun.misc.VM.isBooted())
 183             return;
 184 
 185         synchronized (lock) {
 186             defaultEncoding = "ISO-8859-1";
 187             Properties p = System.getProperties();
 188             p.setProperty("file.encoding", defaultEncoding);
 189             System.setProperties(p);
 190         }
 191     }
 192 
 193     /**
 194      * Get the class that implements the given type of converter for the named
 195      * encoding, or throw an UnsupportedEncodingException if no such class can
 196      * be found
 197      */
 198     private static Class<?> getConverterClass(int type, String encoding)
 199         throws UnsupportedEncodingException
 200     {
 201         String enc = null;
 202 
 203         /* "ISO8859_1" is the canonical name for the ISO-Latin-1 encoding.
 204            Native code in the JDK commonly uses the alias "8859_1" instead of
 205            "ISO8859_1".  We hardwire this alias here in order to avoid loading
 206            the full alias table just for this case. */
 207         if (!encoding.equals("ISO8859_1")) {
 208             if (encoding.equals("8859_1")) {
 209                 enc = "ISO8859_1";
 210             /*
 211              * On Solaris with nl_langinfo() called in GetJavaProperties():
 212              *
 213              *   locale undefined -> NULL -> hardcoded default
 214              *   "C" locale       -> "" -> hardcoded default    (on 2.6)
 215              *   "C" locale       -> "646"                      (on 2.7)
 216              *   "en_US" locale -> "ISO8859-1"
 217              *   "en_GB" locale -> "ISO8859-1"                  (on 2.7)
 218              *   "en_UK" locale -> "ISO8859-1"                  (on 2.6)
 219              */
 220             } else if (encoding.equals("ISO8859-1")) {
 221                 enc = "ISO8859_1";
 222             } else if (encoding.equals("646")) {
 223                 enc = "ASCII";
 224             } else {
 225                 enc = CharacterEncoding.aliasName(encoding);
 226             }
 227         }
 228         if (enc == null) {
 229             enc = encoding;
 230         }
 231 
 232         try {
 233             return Class.forName(getConverterPackageName()
 234                                  + "." + converterPrefix[type] + enc);
 235         } catch(ClassNotFoundException e) {
 236             throw new UnsupportedEncodingException(enc);
 237         }
 238 
 239     }
 240 
 241     /**
 242      * Instantiate the given converter class, or throw an
 243      * UnsupportedEncodingException if it cannot be instantiated
 244      */
 245     private static Object newConverter(String enc, Class<?> c)
 246         throws UnsupportedEncodingException
 247     {
 248         try {
 249             return c.newInstance();
 250         } catch(InstantiationException e) {
 251             throw new UnsupportedEncodingException(enc);
 252         } catch(IllegalAccessException e) {
 253             throw new UnsupportedEncodingException(enc);
 254         }
 255     }
 256 
 257     /**
 258      * Create a converter object that implements the given type of converter
 259      * for the given encoding, or throw an UnsupportedEncodingException if no
 260      * appropriate converter class can be found and instantiated
 261      */
 262     static Object newConverter(int type, String enc)
 263         throws UnsupportedEncodingException
 264     {
 265         Class<?> c;
 266         synchronized (lock) {
 267             c = cache(type, enc);
 268             if (c == null) {
 269                 c = getConverterClass(type, enc);
 270                 if (!c.getName().equals("sun.io.CharToByteUTF8"))
 271                     cache(type, enc, c);
 272             }
 273         }
 274         return newConverter(enc, c);
 275     }
 276 
 277     /**
 278      * Find the class that implements the given type of converter for the
 279      * default encoding.  If the default encoding cannot be determined or is
 280      * not yet defined, return a class that implements the fallback default
 281      * encoding, which is just ISO 8859-1.
 282      */
 283     private static Class<?> getDefaultConverterClass(int type) {
 284         boolean fillCache = false;
 285         Class<?> c;
 286 
 287         /* First check the class cache */
 288         c = cache(type, DEFAULT_NAME);
 289         if (c != null)
 290             return c;
 291 
 292         /* Determine the encoding name */
 293         String enc = getDefaultEncodingName();
 294         if (enc != null) {
 295             /* file.encoding has been set, so cache the converter class */
 296             fillCache = true;
 297         } else {
 298             /* file.encoding has not been set, so use a default encoding which
 299                will not be cached */
 300             enc = "ISO8859_1";
 301         }
 302 
 303         /* We have an encoding name; try to find its class */
 304         try {
 305             c = getConverterClass(type, enc);
 306             if (fillCache) {
 307                 cache(type, DEFAULT_NAME, c);
 308             }
 309         } catch (UnsupportedEncodingException x) {
 310             /* Can't find the default class, so fall back to ISO 8859-1 */
 311             try {
 312                 c = getConverterClass(type, "ISO8859_1");
 313             } catch (UnsupportedEncodingException y) {
 314                 throw new InternalError("Cannot find default "
 315                                         + converterPrefix[type]
 316                                         + " converter class");
 317             }
 318         }
 319         return c;
 320 
 321     }
 322 
 323     /**
 324      * Create a converter object that implements the given type of converter
 325      * for the default encoding, falling back to ISO 8859-1 if the default
 326      * encoding cannot be determined.
 327      */
 328     static Object newDefaultConverter(int type) {
 329         Class<?> c;
 330         synchronized (lock) {
 331             c = getDefaultConverterClass(type);
 332         }
 333         try {
 334             return newConverter("", c);
 335         } catch (UnsupportedEncodingException x) {
 336             throw new InternalError("Cannot instantiate default converter"
 337                                     + " class " + c.getName());
 338         }
 339     }
 340 
 341 }