1 /* 2 * Copyright (c) 2012, 2015, 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.cldr; 27 28 import java.security.AccessController; 29 import java.security.PrivilegedExceptionAction; 30 import java.text.spi.BreakIteratorProvider; 31 import java.text.spi.CollatorProvider; 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Locale; 37 import java.util.Map; 38 import java.util.Objects; 39 import java.util.ServiceLoader; 40 import java.util.Set; 41 import java.util.StringTokenizer; 42 import java.util.concurrent.ConcurrentHashMap; 43 import sun.util.locale.provider.JRELocaleProviderAdapter; 44 import sun.util.locale.provider.LocaleProviderAdapter; 45 import sun.util.locale.provider.LocaleDataMetaInfo; 46 47 /** 48 * LocaleProviderAdapter implementation for the CLDR locale data. 49 * 50 * @author Masayoshi Okutsu 51 * @author Naoto Sato 52 */ 53 public class CLDRLocaleProviderAdapter extends JRELocaleProviderAdapter { 54 55 private static final CLDRBaseLocaleDataMetaInfo baseMetaInfo = new CLDRBaseLocaleDataMetaInfo(); 56 // Assumption: CLDR has only one non-Base module. 57 private final LocaleDataMetaInfo nonBaseMetaInfo; 58 59 // parent locales map 60 private static volatile Map<Locale, Locale> parentLocalesMap; 61 static { 62 parentLocalesMap = new ConcurrentHashMap<>(); 63 // Assuming these locales do NOT have irregular parent locales. 64 parentLocalesMap.put(Locale.ROOT, Locale.ROOT); 65 parentLocalesMap.put(Locale.ENGLISH, Locale.ENGLISH); 66 parentLocalesMap.put(Locale.US, Locale.US); 67 } 68 69 public CLDRLocaleProviderAdapter() { 70 LocaleDataMetaInfo nbmi = null; 71 72 try { 73 nbmi = AccessController.doPrivileged(new PrivilegedExceptionAction<LocaleDataMetaInfo>() { 74 @Override 75 public LocaleDataMetaInfo run() { 76 for (LocaleDataMetaInfo ldmi : ServiceLoader.loadInstalled(LocaleDataMetaInfo.class)) { 77 if (ldmi.getType() == LocaleProviderAdapter.Type.CLDR) { 78 return ldmi; 79 } 80 } 81 return null; 82 } 83 }); 84 } catch (Exception e) { 85 // Catch any exception, and continue as if only CLDR's base locales exist. 86 } 87 88 nonBaseMetaInfo = nbmi; 89 } 90 91 /** 92 * Returns the type of this LocaleProviderAdapter 93 * @return the type of this 94 */ 95 @Override 96 public LocaleProviderAdapter.Type getAdapterType() { 97 return LocaleProviderAdapter.Type.CLDR; 98 } 99 100 @Override 101 public BreakIteratorProvider getBreakIteratorProvider() { 102 return null; 103 } 104 105 @Override 106 public CollatorProvider getCollatorProvider() { 107 return null; 108 } 109 110 @Override 111 public Locale[] getAvailableLocales() { 112 Set<String> all = createLanguageTagSet("AvailableLocales"); 113 Locale[] locs = new Locale[all.size()]; 114 int index = 0; 115 for (String tag : all) { 116 locs[index++] = Locale.forLanguageTag(tag); 117 } 118 return locs; 119 } 120 121 @Override 122 protected Set<String> createLanguageTagSet(String category) { 123 // Directly call Base tags, as we know it's in the base module. 124 String supportedLocaleString = baseMetaInfo.availableLanguageTags(category); 125 String nonBaseTags = null; 126 127 if (nonBaseMetaInfo != null) { 128 nonBaseTags = nonBaseMetaInfo.availableLanguageTags(category); 129 } 130 if (nonBaseTags != null) { 131 if (supportedLocaleString != null) { 132 supportedLocaleString += " " + nonBaseTags; 133 } else { 134 supportedLocaleString = nonBaseTags; 135 } 136 } 137 if (supportedLocaleString == null) { 138 return Collections.emptySet(); 139 } 140 Set<String> tagset = new HashSet<>(); 141 StringTokenizer tokens = new StringTokenizer(supportedLocaleString); 142 while (tokens.hasMoreTokens()) { 143 tagset.add(tokens.nextToken()); 144 } 145 return tagset; 146 } 147 148 // Implementation of ResourceBundleBasedAdapter 149 @Override 150 public List<Locale> getCandidateLocales(String baseName, Locale locale) { 151 List<Locale> candidates = super.getCandidateLocales(baseName, locale); 152 return applyParentLocales(baseName, candidates); 153 } 154 155 private List<Locale> applyParentLocales(String baseName, List<Locale> candidates) { 156 // check irregular parents 157 for (int i = 0; i < candidates.size(); i++) { 158 Locale l = candidates.get(i); 159 if (!l.equals(Locale.ROOT)) { 160 Locale p = getParentLocale(l); 161 if (p != null && 162 !candidates.get(i+1).equals(p)) { 163 List<Locale> applied = candidates.subList(0, i+1); 164 applied.addAll(applyParentLocales(baseName, super.getCandidateLocales(baseName, p))); 165 return applied; 166 } 167 } 168 } 169 170 return candidates; 171 } 172 173 private static Locale getParentLocale(Locale locale) { 174 Locale parent = parentLocalesMap.get(locale); 175 176 if (parent == null) { 177 String tag = locale.toLanguageTag(); 178 for (Map.Entry<Locale, String[]> entry : baseMetaInfo.parentLocales().entrySet()) { 179 if (Arrays.binarySearch(entry.getValue(), tag) >= 0) { 180 parent = entry.getKey(); 181 break; 182 } 183 } 184 if (parent == null) { 185 parent = locale; // non existent marker 186 } 187 parentLocalesMap.putIfAbsent(locale, parent); 188 } 189 190 if (locale.equals(parent)) { 191 // means no irregular parent. 192 parent = null; 193 } 194 195 return parent; 196 } 197 198 @Override 199 public boolean isSupportedProviderLocale(Locale locale, Set<String> langtags) { 200 return Locale.ROOT.equals(locale) || 201 langtags.contains(locale.stripExtensions().toLanguageTag()); 202 } 203 }