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