Home | History | Annotate | Download | only in javaspi
      1 /*
      2  *******************************************************************************
      3  * Copyright (C) 2008-2015, International Business Machines Corporation and
      4  * others. All Rights Reserved.
      5  *******************************************************************************
      6  */
      7 package com.ibm.icu.impl.javaspi;
      8 
      9 import java.io.IOException;
     10 import java.io.InputStream;
     11 import java.util.Arrays;
     12 import java.util.Collections;
     13 import java.util.HashMap;
     14 import java.util.HashSet;
     15 import java.util.Locale;
     16 import java.util.Map;
     17 import java.util.Properties;
     18 import java.util.Set;
     19 
     20 import com.ibm.icu.impl.ICUResourceBundle;
     21 import com.ibm.icu.util.ULocale;
     22 import com.ibm.icu.util.ULocale.Builder;
     23 
     24 public class ICULocaleServiceProvider {
     25     private static final String SPI_PROP_FILE = "com/ibm/icu/impl/javaspi/ICULocaleServiceProviderConfig.properties";
     26 
     27     private static final String SUFFIX_KEY = "com.ibm.icu.impl.javaspi.ICULocaleServiceProvider.icuVariantSuffix";
     28     private static final String ENABLE_VARIANTS_KEY = "com.ibm.icu.impl.javaspi.ICULocaleServiceProvider.enableIcuVariants";
     29     private static final String ENABLE_ISO3_LANG_KEY = "com.ibm.icu.impl.javaspi.ICULocaleServiceProvider.enableIso3Languages";
     30     private static final String USE_DECIMALFORMAT_KEY = "com.ibm.icu.impl.javaspi.ICULocaleServiceProvider.useDecimalFormat";
     31 
     32     private static boolean configLoaded = false;
     33 
     34     private static String suffix = "ICU4J";
     35     private static boolean enableVariants = true;
     36     private static boolean enableIso3Lang = true;
     37     private static boolean useDecimalFormat = false;
     38 
     39     private static final Locale[] SPECIAL_LOCALES = {
     40         new Locale("ja", "JP", "JP"),
     41         new Locale("no"),
     42         new Locale("no", "NO"),
     43         new Locale("no", "NO", "NY"),
     44         new Locale("sr", "CS"),
     45         new Locale("th", "TH", "TH"),
     46     };
     47 
     48     private static Map<Locale, Locale> SPECIAL_LOCALES_MAP = null;
     49 
     50     private static Locale[] LOCALES = null;
     51 
     52     public static Locale[] getAvailableLocales() {
     53         Locale[] all = getLocales();
     54         return Arrays.copyOf(all, all.length);
     55     }
     56 
     57     public static ULocale toULocaleNoSpecialVariant(Locale locale) {
     58         // If the given Locale has legacy ill-formed variant
     59         // reserved by JDK, use the map to resolve the locale.
     60         Locale spLoc = getSpecialLocalesMap().get(locale);
     61         if (spLoc != null) {
     62             return ULocale.forLocale(spLoc);
     63         }
     64 
     65         // The locale may have script field on Java 7+.
     66         // So we once convert it to ULocale, then strip the ICU suffix off
     67         // if necessary.
     68         ULocale result = ULocale.forLocale(locale);
     69         String variant = result.getVariant();
     70         String suffix = getIcuSuffix();
     71         String variantNoSuffix = null;
     72         if (variant.equals(suffix)) {
     73             variantNoSuffix = "";
     74         } else if (variant.endsWith(suffix) && variant.charAt(variant.length() - suffix.length() - 1) == '_') {
     75             variantNoSuffix = variant.substring(0, variant.length() - suffix.length() - 1);
     76         }
     77         if (variantNoSuffix == null) {
     78             return result;
     79         }
     80 
     81         // Strip off ICU's special suffix - cannot use Builder because
     82         // original locale may have ill-formed variant
     83         StringBuilder id = new StringBuilder(result.getLanguage());
     84         String script = result.getScript();
     85         String country = result.getCountry();
     86         if (script.length() > 0) {
     87             id.append('_');
     88             id.append(script);
     89         }
     90         if (country.length() > 0 || variantNoSuffix.length() > 0) {
     91             id.append('_');
     92             id.append(country);
     93         }
     94         if (variantNoSuffix.length() > 0) {
     95             id.append('_');
     96             id.append(variantNoSuffix);
     97         }
     98         String orgID = result.getName();
     99         int kwdIdx = orgID.indexOf('@');
    100         if (kwdIdx >= 0) {
    101             id.append(orgID.substring(kwdIdx));
    102         }
    103         return new ULocale(id.toString());
    104     }
    105 
    106     public static boolean useDecimalFormat() {
    107         loadConfiguration();
    108         return useDecimalFormat;
    109     }
    110 
    111     private static synchronized Map<Locale, Locale> getSpecialLocalesMap() {
    112         if (SPECIAL_LOCALES_MAP != null) {
    113             return SPECIAL_LOCALES_MAP;
    114         }
    115 
    116         Map<Locale, Locale> splocs = new HashMap<Locale, Locale>();
    117         for (Locale spLoc : SPECIAL_LOCALES) {
    118             String var = spLoc.getVariant();
    119             if (var.length() > 0) {
    120                 splocs.put(new Locale(spLoc.getLanguage(), spLoc.getCountry(), var + "_" + getIcuSuffix()), spLoc);
    121             }
    122         }
    123         SPECIAL_LOCALES_MAP = Collections.unmodifiableMap(splocs);
    124         return SPECIAL_LOCALES_MAP;
    125     }
    126 
    127     private static synchronized Locale[] getLocales() {
    128         if (LOCALES != null) {
    129             return LOCALES;
    130         }
    131 
    132         Set<Locale> localeSet = new HashSet<Locale>();
    133         ULocale[] icuLocales = ICUResourceBundle.getAvailableULocales();
    134 
    135         for (ULocale uloc : icuLocales) {
    136             String language = uloc.getLanguage();
    137             if (language.length() >= 3 && !enableIso3Languages()) {
    138                 continue;
    139             }
    140             addULocale(uloc, localeSet);
    141 
    142             if (uloc.getScript().length() > 0 && uloc.getCountry().length() > 0) {
    143                 // ICU's available locales do not contain language+country
    144                 // locales if script is available. Need to add them too.
    145                 Builder locBld = new Builder();
    146                 try {
    147                     locBld.setLocale(uloc);
    148                     locBld.setScript(null);
    149                     ULocale ulocWithoutScript = locBld.build();
    150                     addULocale(ulocWithoutScript, localeSet);
    151                 } catch (Exception e) {
    152                     // ignore
    153                 }
    154             }
    155         }
    156 
    157         for (Locale l : SPECIAL_LOCALES) {
    158             addLocale(l, localeSet);
    159         }
    160 
    161         LOCALES = localeSet.toArray(new Locale[0]);
    162         return LOCALES;
    163     }
    164 
    165     private static void addLocale(Locale loc, Set<Locale> locales) {
    166         locales.add(loc);
    167 
    168         if (enableIcuVariants()) {
    169             // Add ICU variant
    170             String language = loc.getLanguage();
    171             String country = loc.getCountry();
    172             String variant = loc.getVariant();
    173 
    174             StringBuilder var = new StringBuilder(variant);
    175             if (var.length() != 0) {
    176                 var.append("_");
    177             }
    178             var.append(getIcuSuffix());
    179             locales.add(new Locale(language, country, var.toString()));
    180         }
    181     }
    182 
    183     private static void addULocale(ULocale uloc, Set<Locale> locales) {
    184         // special case - nn
    185         // ULocale#toLocale on Java 6 maps "nn" to "no_NO_NY"
    186         if (uloc.getLanguage().equals("nn") && uloc.getScript().length() == 0) {
    187             Locale locNN = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant());
    188             addLocale(locNN, locales);
    189             return;
    190         }
    191 
    192         locales.add(uloc.toLocale());
    193 
    194         if (enableIcuVariants()) {
    195             // Add ICU variant
    196             StringBuilder var = new StringBuilder(uloc.getVariant());
    197             if (var.length() != 0) {
    198                 var.append("_");
    199             }
    200             var.append(getIcuSuffix());
    201 
    202             Builder locBld = new Builder();
    203             try {
    204                 locBld.setLocale(uloc);
    205                 locBld.setVariant(var.toString());
    206                 ULocale ulocWithVar = locBld.build();
    207                 locales.add(ulocWithVar.toLocale());
    208             } catch (Exception e) {
    209                 // ignore
    210             }
    211         }
    212     }
    213 
    214     private static boolean enableIso3Languages() {
    215         return enableIso3Lang;
    216     }
    217 
    218     private static boolean enableIcuVariants() {
    219         loadConfiguration();
    220         return enableVariants;
    221     }
    222 
    223     private static String getIcuSuffix() {
    224         loadConfiguration();
    225         return suffix;
    226     }
    227 
    228     private static synchronized void loadConfiguration() {
    229         if (configLoaded) {
    230             return;
    231         }
    232         Properties spiConfigProps = new Properties();
    233         try {
    234             InputStream is = ClassLoader.getSystemResourceAsStream(SPI_PROP_FILE);
    235             try {
    236                 spiConfigProps.load(is);
    237             } finally {
    238                 is.close();
    239             }
    240 
    241             String val = (String)spiConfigProps.get(SUFFIX_KEY);
    242             if (val != null && val.length() > 0) {
    243                 suffix = val;
    244             }
    245             enableVariants = parseBooleanString((String)spiConfigProps.get(ENABLE_VARIANTS_KEY), enableVariants);
    246             enableIso3Lang = parseBooleanString((String)spiConfigProps.get(ENABLE_ISO3_LANG_KEY), enableIso3Lang);
    247             useDecimalFormat = parseBooleanString((String)spiConfigProps.get(USE_DECIMALFORMAT_KEY), useDecimalFormat);
    248         } catch (IOException ioe) {
    249             // Any IO errors, ignore
    250         }
    251         configLoaded = true;
    252     }
    253 
    254     private static boolean parseBooleanString(String str, boolean defaultVal) {
    255         if (str == null) {
    256             return defaultVal;
    257         }
    258         if (str.equalsIgnoreCase("true")) {
    259             return true;
    260         } else if (str.equalsIgnoreCase("false")) {
    261             return false;
    262         }
    263         return defaultVal;
    264     }
    265 }
    266