1 /* 2 * Copyright (c) 1996, 2014, 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.Arrays; 46 import java.util.Iterator; 47 import java.util.List; 48 import java.util.Locale; 49 import java.util.MissingResourceException; 50 import java.util.ResourceBundle; 51 import java.util.Set; 52 import sun.util.locale.provider.JRELocaleProviderAdapter; 53 import sun.util.locale.provider.LocaleProviderAdapter; 54 import sun.util.locale.provider.ResourceBundleBasedAdapter; 55 import static sun.util.locale.provider.LocaleProviderAdapter.Type.CLDR; 56 import static sun.util.locale.provider.LocaleProviderAdapter.Type.JRE; 57 58 /** 59 * Provides information about and access to resource bundles in the 60 * sun.text.resources and sun.util.resources packages or in their corresponding 61 * packages for CLDR. 62 * 63 * @author Asmus Freytag 64 * @author Mark Davis 65 */ 66 67 public class LocaleData { 68 private final LocaleProviderAdapter.Type type; 69 70 public LocaleData(LocaleProviderAdapter.Type type) { 71 this.type = type; 72 } 73 74 /** 75 * Gets a calendar data resource bundle, using privileges 76 * to allow accessing a sun.* package. 77 */ 78 public ResourceBundle getCalendarData(Locale locale) { 79 return getBundle(type.getUtilResourcesPackage() + ".CalendarData", locale); 80 } 81 82 /** 83 * Gets a currency names resource bundle, using privileges 84 * to allow accessing a sun.* package. 85 */ 86 public OpenListResourceBundle getCurrencyNames(Locale locale) { 87 return (OpenListResourceBundle) getBundle(type.getUtilResourcesPackage() + ".CurrencyNames", locale); 88 } 89 90 /** 91 * Gets a locale names resource bundle, using privileges 92 * to allow accessing a sun.* package. 93 */ 94 public OpenListResourceBundle getLocaleNames(Locale locale) { 95 return (OpenListResourceBundle) getBundle(type.getUtilResourcesPackage() + ".LocaleNames", locale); 96 } 97 98 /** 99 * Gets a time zone names resource bundle, using privileges 100 * to allow accessing a sun.* package. 101 */ 102 public TimeZoneNamesBundle getTimeZoneNames(Locale locale) { 103 return (TimeZoneNamesBundle) getBundle(type.getUtilResourcesPackage() + ".TimeZoneNames", locale); 104 } 105 106 /** 107 * Gets a break iterator info resource bundle, using privileges 108 * to allow accessing a sun.* package. 109 */ 110 public ResourceBundle getBreakIteratorInfo(Locale locale) { 111 return getBundle(type.getTextResourcesPackage() + ".BreakIteratorInfo", locale); 112 } 113 114 /** 115 * Gets a collation data resource bundle, using privileges 116 * to allow accessing a sun.* package. 117 */ 118 public ResourceBundle getCollationData(Locale locale) { 119 return getBundle(type.getTextResourcesPackage() + ".CollationData", locale); 120 } 121 122 /** 123 * Gets a date format data resource bundle, using privileges 124 * to allow accessing a sun.* package. 125 */ 126 public ResourceBundle getDateFormatData(Locale locale) { 127 return getBundle(type.getTextResourcesPackage() + ".FormatData", locale); 128 } 129 130 public void setSupplementary(ParallelListResourceBundle formatData) { 131 if (!formatData.areParallelContentsComplete()) { 132 String suppName = type.getTextResourcesPackage() + ".JavaTimeSupplementary"; 133 setSupplementary(suppName, formatData); 134 } 135 } 136 137 private boolean setSupplementary(String suppName, ParallelListResourceBundle formatData) { 138 ParallelListResourceBundle parent = (ParallelListResourceBundle) formatData.getParent(); 139 boolean resetKeySet = false; 140 if (parent != null) { 141 resetKeySet = setSupplementary(suppName, parent); 142 } 143 OpenListResourceBundle supp = getSupplementary(suppName, formatData.getLocale()); 144 formatData.setParallelContents(supp); 145 resetKeySet |= supp != null; 146 // If any parents or this bundle has parallel data, reset keyset to create 147 // a new keyset with the data. 148 if (resetKeySet) { 149 formatData.resetKeySet(); 150 } 151 return resetKeySet; 152 } 153 154 /** 155 * Gets a number format data resource bundle, using privileges 156 * to allow accessing a sun.* package. 157 */ 158 public ResourceBundle getNumberFormatData(Locale locale) { 159 return getBundle(type.getTextResourcesPackage() + ".FormatData", locale); 160 } 161 162 public static ResourceBundle getBundle(final String baseName, final Locale locale) { 163 return AccessController.doPrivileged(new PrivilegedAction<>() { 164 @Override 165 public ResourceBundle run() { 166 return ResourceBundle 167 .getBundle(baseName, locale, LocaleDataResourceBundleControl.INSTANCE); 168 } 169 }); 170 } 171 172 private static OpenListResourceBundle getSupplementary(final String baseName, final Locale locale) { 173 return AccessController.doPrivileged(new PrivilegedAction<>() { 174 @Override 175 public OpenListResourceBundle run() { 176 OpenListResourceBundle rb = null; 177 try { 178 rb = (OpenListResourceBundle) ResourceBundle.getBundle(baseName, 179 locale, SupplementaryResourceBundleControl.INSTANCE); 180 181 } catch (MissingResourceException e) { 182 // return null if no supplementary is available 183 } 184 return rb; 185 } 186 }); 187 } 188 189 private static class LocaleDataResourceBundleControl extends ResourceBundle.Control { 190 /* Singlton instance of ResourceBundle.Control. */ 191 private static final LocaleDataResourceBundleControl INSTANCE = 192 new LocaleDataResourceBundleControl(); 193 194 private LocaleDataResourceBundleControl() { 195 } 196 197 /* 198 * This method overrides the default implementation to search 199 * from a prebaked locale string list to determin the candidate 200 * locale list. 201 * 202 * @param baseName the resource bundle base name. 203 * locale the requested locale for the resource bundle. 204 * @returns a list of candidate locales to search from. 205 * @exception NullPointerException if baseName or locale is null. 206 */ 207 @Override 208 public List<Locale> getCandidateLocales(String baseName, Locale locale) { 209 LocaleProviderAdapter.Type type = baseName.contains(DOTCLDR) ? CLDR : JRE; 210 LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); 211 List<Locale> candidates = adapter instanceof ResourceBundleBasedAdapter ? 212 ((ResourceBundleBasedAdapter)adapter).getCandidateLocales(baseName, locale) : 213 super.getCandidateLocales(baseName, locale); 214 215 // Weed out Locales which are known to have no resource bundles 216 int lastDot = baseName.lastIndexOf('.'); 217 String category = (lastDot >= 0) ? baseName.substring(lastDot + 1) : baseName; 218 Set<String> langtags = ((JRELocaleProviderAdapter)adapter).getLanguageTagSet(category); 219 if (!langtags.isEmpty()) { 220 for (Iterator<Locale> itr = candidates.iterator(); itr.hasNext();) { 221 if (!adapter.isSupportedProviderLocale(itr.next(), langtags)) { 222 itr.remove(); 223 } 224 } 225 } 226 227 // Force fallback to Locale.ENGLISH for CLDR time zone names support 228 if (locale.getLanguage() != "en" 229 && type == CLDR && category.equals("TimeZoneNames")) { 230 candidates.add(candidates.size() - 1, Locale.ENGLISH); 231 } 232 return candidates; 233 } 234 235 /* 236 * Overrides "getFallbackLocale" to return null so 237 * that the fallback locale will be null. 238 * @param baseName the resource bundle base name. 239 * locale the requested locale for the resource bundle. 240 * @return null for the fallback locale. 241 * @exception NullPointerException if baseName or locale is null. 242 */ 243 @Override 244 public Locale getFallbackLocale(String baseName, Locale locale) { 245 if (baseName == null || locale == null) { 246 throw new NullPointerException(); 247 } 248 return null; 249 } 250 251 private static final String DOTCLDR = ".cldr"; 252 253 /** 254 * Changes baseName to its per-language/country package name and 255 * calls the super class implementation. For example, 256 * if the baseName is "sun.text.resources.FormatData" and locale is ja_JP, 257 * the baseName is changed to "sun.text.resources.ja.JP.FormatData". If 258 * baseName contains "cldr", such as "sun.text.resources.cldr.FormatData", 259 * the name is changed to "sun.text.resources.cldr.ja.JP.FormatData". 260 */ 261 @Override 262 public String toBundleName(String baseName, Locale locale) { 263 String newBaseName = baseName; 264 String lang = locale.getLanguage(); 265 String ctry = locale.getCountry(); 266 if (lang.length() > 0) { 267 if (baseName.startsWith(JRE.getUtilResourcesPackage()) 268 || baseName.startsWith(JRE.getTextResourcesPackage())) { 269 // Assume the lengths are the same. 270 assert JRE.getUtilResourcesPackage().length() 271 == JRE.getTextResourcesPackage().length(); 272 int index = JRE.getUtilResourcesPackage().length(); 273 if (baseName.indexOf(DOTCLDR, index) > 0) { 274 index += DOTCLDR.length(); 275 } 276 ctry = (ctry.length() == 2) ? ("." + ctry) : ""; 277 newBaseName = baseName.substring(0, index + 1) + lang + ctry 278 + baseName.substring(index); 279 } 280 } 281 return super.toBundleName(newBaseName, locale); 282 } 283 } 284 285 private static class SupplementaryResourceBundleControl extends LocaleDataResourceBundleControl { 286 private static final SupplementaryResourceBundleControl INSTANCE = 287 new SupplementaryResourceBundleControl(); 288 289 private SupplementaryResourceBundleControl() { 290 } 291 292 @Override 293 public List<Locale> getCandidateLocales(String baseName, Locale locale) { 294 // Specifiy only the given locale 295 return Arrays.asList(locale); 296 } 297 298 @Override 299 public long getTimeToLive(String baseName, Locale locale) { 300 assert baseName.contains("JavaTimeSupplementary"); 301 return TTL_DONT_CACHE; 302 } 303 } 304 }