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