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