1 /*
   2  * Copyright (c) 1996, 2012, 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 /*
  27  * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
  28  * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
  29  *
  30  * The original version of this source code and documentation
  31  * is copyrighted and owned by Taligent, Inc., a wholly-owned
  32  * subsidiary of IBM. These materials are provided under terms
  33  * of a License Agreement between Taligent and Sun. This technology
  34  * is protected by multiple US and International patents.
  35  *
  36  * This notice and attribution to Taligent may not be removed.
  37  * Taligent is a registered trademark of Taligent, Inc.
  38  *
  39  */
  40 
  41 package sun.util.resources;
  42 
  43 import java.security.AccessController;
  44 import java.security.PrivilegedAction;
  45 import java.util.Iterator;
  46 import java.util.List;
  47 import java.util.Locale;
  48 import java.util.ResourceBundle;
  49 import sun.util.locale.provider.LocaleDataMetaInfo;
  50 import sun.util.locale.provider.LocaleProviderAdapter;
  51 import static sun.util.locale.provider.LocaleProviderAdapter.Type.JRE;
  52 
  53 /**
  54  * Provides information about and access to resource bundles in the
  55  * sun.text.resources and sun.util.resources packages or in their corresponding
  56  * packages for CLDR.
  57  *
  58  * @author Asmus Freytag
  59  * @author Mark Davis
  60  */
  61 
  62 public class LocaleData {
  63     private final LocaleProviderAdapter.Type type;
  64 
  65     public LocaleData(LocaleProviderAdapter.Type type) {
  66         this.type = type;
  67     }
  68 
  69     /**
  70      * Gets a calendar data resource bundle, using privileges
  71      * to allow accessing a sun.* package.
  72      */
  73     public ResourceBundle getCalendarData(Locale locale) {
  74         return getBundle(type.getUtilResourcesPackage() + ".CalendarData", locale);
  75     }
  76 
  77     /**
  78      * Gets a currency names resource bundle, using privileges
  79      * to allow accessing a sun.* package.
  80      */
  81     public OpenListResourceBundle getCurrencyNames(Locale locale) {
  82         return (OpenListResourceBundle) getBundle(type.getUtilResourcesPackage() + ".CurrencyNames", locale);
  83     }
  84 
  85     /**
  86      * Gets a locale names resource bundle, using privileges
  87      * to allow accessing a sun.* package.
  88      */
  89     public OpenListResourceBundle getLocaleNames(Locale locale) {
  90         return (OpenListResourceBundle) getBundle(type.getUtilResourcesPackage() + ".LocaleNames", locale);
  91     }
  92 
  93     /**
  94      * Gets a time zone names resource bundle, using privileges
  95      * to allow accessing a sun.* package.
  96      */
  97     public TimeZoneNamesBundle getTimeZoneNames(Locale locale) {
  98         return (TimeZoneNamesBundle) getBundle(type.getUtilResourcesPackage() + ".TimeZoneNames", locale);
  99     }
 100 
 101     /**
 102      * Gets a break iterator info resource bundle, using privileges
 103      * to allow accessing a sun.* package.
 104      */
 105     public ResourceBundle getBreakIteratorInfo(Locale locale) {
 106         return getBundle(type.getTextResourcesPackage() + ".BreakIteratorInfo", locale);
 107     }
 108 
 109     /**
 110      * Gets a collation data resource bundle, using privileges
 111      * to allow accessing a sun.* package.
 112      */
 113     public ResourceBundle getCollationData(Locale locale) {
 114         return getBundle(type.getTextResourcesPackage() + ".CollationData", locale);
 115     }
 116 
 117     /**
 118      * Gets a date format data resource bundle, using privileges
 119      * to allow accessing a sun.* package.
 120      */
 121     public ResourceBundle getDateFormatData(Locale locale) {
 122         return getBundle(type.getTextResourcesPackage() + ".FormatData", locale);
 123     }
 124 
 125     /**
 126      * Gets a number format data resource bundle, using privileges
 127      * to allow accessing a sun.* package.
 128      */
 129     public ResourceBundle getNumberFormatData(Locale locale) {
 130         return getBundle(type.getTextResourcesPackage() + ".FormatData", locale);
 131     }
 132 
 133     public static ResourceBundle getBundle(final String baseName, final Locale locale) {
 134         return AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
 135                 @Override
 136                 public ResourceBundle run() {
 137                     return ResourceBundle.
 138                         getBundle(baseName, locale,
 139                                   LocaleDataResourceBundleControl.getRBControlInstance());
 140                 }
 141             });
 142     }
 143 
 144     private static class LocaleDataResourceBundleControl extends ResourceBundle.Control {
 145         /* Singlton instance of ResourceBundle.Control. */
 146         private static LocaleDataResourceBundleControl rbControlInstance =
 147             new LocaleDataResourceBundleControl();
 148 
 149         public static LocaleDataResourceBundleControl getRBControlInstance() {
 150             return rbControlInstance;
 151         }
 152 
 153         /*
 154          * This method overrides the default implementation to search
 155          * from a prebaked locale string list to determin the candidate
 156          * locale list.
 157          *
 158          * @param baseName the resource bundle base name.
 159          *        locale   the requested locale for the resource bundle.
 160          * @returns a list of candidate locales to search from.
 161          * @exception NullPointerException if baseName or locale is null.
 162          */
 163         @Override
 164          public List<Locale> getCandidateLocales(String baseName, Locale locale) {
 165             List<Locale> candidates = super.getCandidateLocales(baseName, locale);
 166             /* Get the locale string list from LocaleDataMetaInfo class. */
 167             String localeString = LocaleDataMetaInfo.getSupportedLocaleString(baseName);
 168 
 169             if (localeString != null && localeString.length() != 0) {
 170                 for (Iterator<Locale> l = candidates.iterator(); l.hasNext();) {
 171                     Locale loc = l.next();
 172                     String lstr;
 173                     if (loc.getScript().length() > 0) {
 174                         lstr = loc.toLanguageTag().replace('-', '_');
 175                     } else {
 176                         lstr = loc.toString();
 177                         int idx = lstr.indexOf("_#");
 178                         if (idx >= 0) {
 179                             lstr = lstr.substring(0, idx);
 180                         }
 181                     }
 182                     /* Every locale string in the locale string list returned from
 183                      the above getSupportedLocaleString is enclosed
 184                      within two white spaces so that we could check some locale
 185                      such as "en".
 186                      */
 187                     if (lstr.length() != 0 && localeString.indexOf(" " + lstr + " ") == -1) {
 188                         l.remove();
 189                     }
 190                 }
 191             }
 192             // Force fallback to Locale.ENGLISH for CLDR time zone names support
 193             if (locale.getLanguage() != "en"
 194                     && baseName.contains(CLDR) && baseName.endsWith("TimeZoneNames")) {
 195                 candidates.add(candidates.size() - 1, Locale.ENGLISH);
 196             }
 197             return candidates;
 198         }
 199 
 200         /*
 201          * Overrides "getFallbackLocale" to return null so
 202          * that the fallback locale will be null.
 203          * @param baseName the resource bundle base name.
 204          *        locale   the requested locale for the resource bundle.
 205          * @return null for the fallback locale.
 206          * @exception NullPointerException if baseName or locale is null.
 207          */
 208         @Override
 209         public Locale getFallbackLocale(String baseName, Locale locale) {
 210             if (baseName == null || locale == null) {
 211                 throw new NullPointerException();
 212             }
 213             return null;
 214         }
 215 
 216         private static final String CLDR      = ".cldr";
 217 
 218         /**
 219          * Changes baseName to its per-language package name and
 220          * calls the super class implementation. For example,
 221          * if the baseName is "sun.text.resources.FormatData" and locale is ja_JP,
 222          * the baseName is changed to "sun.text.resources.ja.FormatData". If
 223          * baseName contains "cldr", such as "sun.text.resources.cldr.FormatData",
 224          * the name is changed to "sun.text.resources.cldr.jp.FormatData".
 225          */
 226         @Override
 227         public String toBundleName(String baseName, Locale locale) {
 228             String newBaseName = baseName;
 229             String lang = locale.getLanguage();
 230             if (lang.length() > 0) {
 231                 if (baseName.startsWith(JRE.getUtilResourcesPackage())
 232                         || baseName.startsWith(JRE.getTextResourcesPackage())) {
 233                     // Assume the lengths are the same.
 234                     assert JRE.getUtilResourcesPackage().length()
 235                         == JRE.getTextResourcesPackage().length();
 236                     int index = JRE.getUtilResourcesPackage().length();
 237                     if (baseName.indexOf(CLDR, index) > 0) {
 238                         index += CLDR.length();
 239                     }
 240                     newBaseName = baseName.substring(0, index + 1) + lang
 241                                       + baseName.substring(index);
 242                 }
 243             }
 244             return super.toBundleName(newBaseName, locale);
 245         }
 246 
 247     }
 248 }