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