1 /* 2 * Copyright (c) 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 package sun.util.locale.provider; 27 28 import java.io.File; 29 import java.security.AccessController; 30 import java.security.PrivilegedAction; 31 import java.text.spi.BreakIteratorProvider; 32 import java.text.spi.CollatorProvider; 33 import java.text.spi.DateFormatProvider; 34 import java.text.spi.DateFormatSymbolsProvider; 35 import java.text.spi.DecimalFormatSymbolsProvider; 36 import java.text.spi.NumberFormatProvider; 37 import java.util.HashSet; 38 import java.util.Locale; 39 import java.util.Set; 40 import java.util.StringTokenizer; 41 import java.util.concurrent.ConcurrentHashMap; 42 import java.util.concurrent.ConcurrentMap; 43 import java.util.spi.CalendarDataProvider; 44 import java.util.spi.CalendarNameProvider; 45 import java.util.spi.CurrencyNameProvider; 46 import java.util.spi.LocaleNameProvider; 47 import java.util.spi.LocaleServiceProvider; 48 import java.util.spi.TimeZoneNameProvider; 49 import sun.util.resources.LocaleData; 50 51 /** 52 * LocaleProviderAdapter implementation for the legacy JRE locale data. 53 * 54 * @author Naoto Sato 55 * @author Masayoshi Okutsu 56 */ 57 public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter { 58 59 private static final String LOCALE_DATA_JAR_NAME = "localedata.jar"; 60 61 private final ConcurrentMap<String, Set<String>> langtagSets 62 = new ConcurrentHashMap<>(); 63 64 private final ConcurrentMap<Locale, LocaleResources> localeResourcesMap 65 = new ConcurrentHashMap<>(); 66 67 // LocaleData specific to this LocaleProviderAdapter. 68 private volatile LocaleData localeData; 69 70 /** 71 * Returns the type of this LocaleProviderAdapter 72 */ 73 @Override 74 public LocaleProviderAdapter.Type getAdapterType() { 75 return Type.JRE; 76 } 77 78 /** 79 * Getter method for Locale Service Providers 80 */ 81 @Override 82 @SuppressWarnings("unchecked") 83 public <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c) { 84 switch (c.getSimpleName()) { 85 case "BreakIteratorProvider": 86 return (P) getBreakIteratorProvider(); 87 case "CollatorProvider": 88 return (P) getCollatorProvider(); 89 case "DateFormatProvider": 90 return (P) getDateFormatProvider(); 91 case "DateFormatSymbolsProvider": 92 return (P) getDateFormatSymbolsProvider(); 93 case "DecimalFormatSymbolsProvider": 94 return (P) getDecimalFormatSymbolsProvider(); 95 case "NumberFormatProvider": 96 return (P) getNumberFormatProvider(); 97 case "CurrencyNameProvider": 98 return (P) getCurrencyNameProvider(); 99 case "LocaleNameProvider": 100 return (P) getLocaleNameProvider(); 101 case "TimeZoneNameProvider": 102 return (P) getTimeZoneNameProvider(); 103 case "CalendarDataProvider": 104 return (P) getCalendarDataProvider(); 105 case "CalendarNameProvider": 106 return (P) getCalendarNameProvider(); 107 default: 108 throw new InternalError("should not come down here"); 109 } 110 } 111 112 private volatile BreakIteratorProvider breakIteratorProvider = null; 113 private volatile CollatorProvider collatorProvider = null; 114 private volatile DateFormatProvider dateFormatProvider = null; 115 private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider = null; 116 private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider = null; 117 private volatile NumberFormatProvider numberFormatProvider = null; 118 119 private volatile CurrencyNameProvider currencyNameProvider = null; 120 private volatile LocaleNameProvider localeNameProvider = null; 121 private volatile TimeZoneNameProvider timeZoneNameProvider = null; 122 private volatile CalendarDataProvider calendarDataProvider = null; 123 private volatile CalendarNameProvider calendarNameProvider = null; 124 125 /* 126 * Getter methods for java.text.spi.* providers 127 */ 128 @Override 129 public BreakIteratorProvider getBreakIteratorProvider() { 130 if (breakIteratorProvider == null) { 131 BreakIteratorProvider provider = new BreakIteratorProviderImpl(getAdapterType(), 132 getLanguageTagSet("FormatData")); 133 synchronized (this) { 134 if (breakIteratorProvider == null) { 135 breakIteratorProvider = provider; 136 } 137 } 138 } 139 return breakIteratorProvider; 140 } 141 142 @Override 143 public CollatorProvider getCollatorProvider() { 144 if (collatorProvider == null) { 145 CollatorProvider provider = new CollatorProviderImpl(getAdapterType(), 146 getLanguageTagSet("CollationData")); 147 synchronized (this) { 148 if (collatorProvider == null) { 149 collatorProvider = provider; 150 } 151 } 152 } 153 return collatorProvider; 154 } 155 156 @Override 157 public DateFormatProvider getDateFormatProvider() { 158 if (dateFormatProvider == null) { 159 DateFormatProvider provider = new DateFormatProviderImpl(getAdapterType(), 160 getLanguageTagSet("FormatData")); 161 synchronized (this) { 162 if (dateFormatProvider == null) { 163 dateFormatProvider = provider; 164 } 165 } 166 } 167 return dateFormatProvider; 168 } 169 170 @Override 171 public DateFormatSymbolsProvider getDateFormatSymbolsProvider() { 172 if (dateFormatSymbolsProvider == null) { 173 DateFormatSymbolsProvider provider = new DateFormatSymbolsProviderImpl(getAdapterType(), 174 getLanguageTagSet("FormatData")); 175 synchronized (this) { 176 if (dateFormatSymbolsProvider == null) { 177 dateFormatSymbolsProvider = provider; 178 } 179 } 180 } 181 return dateFormatSymbolsProvider; 182 } 183 184 @Override 185 public DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() { 186 if (decimalFormatSymbolsProvider == null) { 187 DecimalFormatSymbolsProvider provider = new DecimalFormatSymbolsProviderImpl(getAdapterType(), getLanguageTagSet("FormatData")); 188 synchronized (this) { 189 if (decimalFormatSymbolsProvider == null) { 190 decimalFormatSymbolsProvider = provider; 191 } 192 } 193 } 194 return decimalFormatSymbolsProvider; 195 } 196 197 @Override 198 public NumberFormatProvider getNumberFormatProvider() { 199 if (numberFormatProvider == null) { 200 NumberFormatProvider provider = new NumberFormatProviderImpl(getAdapterType(), 201 getLanguageTagSet("FormatData")); 202 synchronized (this) { 203 if (numberFormatProvider == null) { 204 numberFormatProvider = provider; 205 } 206 } 207 } 208 return numberFormatProvider; 209 } 210 211 /** 212 * Getter methods for java.util.spi.* providers 213 */ 214 @Override 215 public CurrencyNameProvider getCurrencyNameProvider() { 216 if (currencyNameProvider == null) { 217 CurrencyNameProvider provider = new CurrencyNameProviderImpl(getAdapterType(), 218 getLanguageTagSet("CurrencyNames")); 219 synchronized (this) { 220 if (currencyNameProvider == null) { 221 currencyNameProvider = provider; 222 } 223 } 224 } 225 return currencyNameProvider; 226 } 227 228 @Override 229 public LocaleNameProvider getLocaleNameProvider() { 230 if (localeNameProvider == null) { 231 LocaleNameProvider provider = new LocaleNameProviderImpl(getAdapterType(), 232 getLanguageTagSet("LocaleNames")); 233 synchronized (this) { 234 if (localeNameProvider == null) { 235 localeNameProvider = provider; 236 } 237 } 238 } 239 return localeNameProvider; 240 } 241 242 @Override 243 public TimeZoneNameProvider getTimeZoneNameProvider() { 244 if (timeZoneNameProvider == null) { 245 TimeZoneNameProvider provider = new TimeZoneNameProviderImpl(getAdapterType(), 246 getLanguageTagSet("TimeZoneNames")); 247 synchronized (this) { 248 if (timeZoneNameProvider == null) { 249 timeZoneNameProvider = provider; 250 } 251 } 252 } 253 return timeZoneNameProvider; 254 } 255 256 @Override 257 public CalendarDataProvider getCalendarDataProvider() { 258 if (calendarDataProvider == null) { 259 CalendarDataProvider provider; 260 provider = new CalendarDataProviderImpl(getAdapterType(), 261 getLanguageTagSet("CalendarData")); 262 synchronized (this) { 263 if (calendarDataProvider == null) { 264 calendarDataProvider = provider; 265 } 266 } 267 } 268 return calendarDataProvider; 269 } 270 271 @Override 272 public CalendarNameProvider getCalendarNameProvider() { 273 if (calendarNameProvider == null) { 274 CalendarNameProvider provider; 275 provider = new CalendarNameProviderImpl(getAdapterType(), 276 getLanguageTagSet("FormatData")); 277 synchronized (this) { 278 if (calendarNameProvider == null) { 279 calendarNameProvider = provider; 280 } 281 } 282 } 283 return calendarNameProvider; 284 } 285 286 @Override 287 public LocaleResources getLocaleResources(Locale locale) { 288 LocaleResources lr = localeResourcesMap.get(locale); 289 if (lr == null) { 290 lr = new LocaleResources(this, locale); 291 LocaleResources lrc = localeResourcesMap.putIfAbsent(locale, lr); 292 if (lrc != null) { 293 lr = lrc; 294 } 295 } 296 return lr; 297 } 298 299 // ResourceBundleBasedAdapter method implementation 300 @Override 301 public LocaleData getLocaleData() { 302 if (localeData == null) { 303 synchronized (this) { 304 if (localeData == null) { 305 localeData = new LocaleData(getAdapterType()); 306 } 307 } 308 } 309 return localeData; 310 } 311 312 /** 313 * Returns a list of the installed locales. Currently, this simply returns 314 * the list of locales for which a sun.text.resources.FormatData bundle 315 * exists. This bundle family happens to be the one with the broadest 316 * locale coverage in the JRE. 317 */ 318 @Override 319 public Locale[] getAvailableLocales() { 320 return AvailableJRELocales.localeList.clone(); 321 } 322 323 public Set<String> getLanguageTagSet(String category) { 324 Set<String> tagset = langtagSets.get(category); 325 if (tagset == null) { 326 tagset = createLanguageTagSet(category); 327 Set<String> ts = langtagSets.putIfAbsent(category, tagset); 328 if (ts != null) { 329 tagset = ts; 330 } 331 } 332 return tagset; 333 } 334 335 protected Set<String> createLanguageTagSet(String category) { 336 String supportedLocaleString = LocaleDataMetaInfo.getSupportedLocaleString(category); 337 Set<String> tagset = new HashSet<>(); 338 StringTokenizer tokens = new StringTokenizer(supportedLocaleString); 339 while (tokens.hasMoreTokens()) { 340 String token = tokens.nextToken(); 341 if (token.equals("|")) { 342 if (isNonUSLangSupported()) { 343 continue; 344 } 345 break; 346 } 347 tagset.add(token); 348 } 349 350 return tagset; 351 } 352 353 /** 354 * Lazy load available locales. 355 */ 356 private static class AvailableJRELocales { 357 private static final Locale[] localeList = createAvailableLocales(); 358 private AvailableJRELocales() { 359 } 360 } 361 362 private static Locale[] createAvailableLocales() { 363 /* 364 * Gets the locale string list from LocaleDataMetaInfo class and then 365 * contructs the Locale array and a set of language tags based on the 366 * locale string returned above. 367 */ 368 String supportedLocaleString = LocaleDataMetaInfo.getSupportedLocaleString("AvailableLocales"); 369 370 if (supportedLocaleString.length() == 0) { 371 throw new InternalError("No available locales for JRE"); 372 } 373 374 /* 375 * Look for "|" and construct a new locale string list. 376 */ 377 int barIndex = supportedLocaleString.indexOf('|'); 378 StringTokenizer localeStringTokenizer; 379 if (isNonUSLangSupported()) { 380 localeStringTokenizer = new StringTokenizer(supportedLocaleString.substring(0, barIndex) 381 + supportedLocaleString.substring(barIndex + 1)); 382 } else { 383 localeStringTokenizer = new StringTokenizer(supportedLocaleString.substring(0, barIndex)); 384 } 385 386 int length = localeStringTokenizer.countTokens(); 387 Locale[] locales = new Locale[length + 1]; 388 locales[0] = Locale.ROOT; 389 for (int i = 1; i <= length; i++) { 390 String currentToken = localeStringTokenizer.nextToken(); 391 switch (currentToken) { 392 case "ja-JP-JP": 393 locales[i] = JRELocaleConstants.JA_JP_JP; 394 break; 395 case "no-NO-NY": 396 locales[i] = JRELocaleConstants.NO_NO_NY; 397 break; 398 case "th-TH-TH": 399 locales[i] = JRELocaleConstants.TH_TH_TH; 400 break; 401 default: 402 locales[i] = Locale.forLanguageTag(currentToken); 403 } 404 } 405 return locales; 406 } 407 408 private static volatile Boolean isNonUSSupported = null; 409 410 /* 411 * Returns true if the non US resources jar file exists in jre 412 * extension directory. @returns true if the jar file is there. Otherwise, 413 * returns false. 414 */ 415 private static boolean isNonUSLangSupported() { 416 if (isNonUSSupported == null) { 417 synchronized (JRELocaleProviderAdapter.class) { 418 if (isNonUSSupported == null) { 419 final String sep = File.separator; 420 String localeDataJar = 421 java.security.AccessController.doPrivileged( 422 new sun.security.action.GetPropertyAction("java.home")) 423 + sep + "lib" + sep + "ext" + sep + LOCALE_DATA_JAR_NAME; 424 425 /* 426 * Peek at the installed extension directory to see if 427 * localedata.jar is installed or not. 428 */ 429 final File f = new File(localeDataJar); 430 isNonUSSupported = 431 AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 432 @Override 433 public Boolean run() { 434 return f.exists(); 435 } 436 }); 437 } 438 } 439 } 440 return isNonUSSupported; 441 } 442 }