1 package org.unicode.cldr.tool; 2 3 import java.util.Arrays; 4 import java.util.Collections; 5 import java.util.Date; 6 import java.util.HashMap; 7 import java.util.List; 8 import java.util.Map; 9 import java.util.Set; 10 import java.util.TreeSet; 11 12 import org.unicode.cldr.util.Builder; 13 import org.unicode.cldr.util.LanguageTagParser; 14 import org.unicode.cldr.util.LanguageTagParser.OutputOption; 15 import org.unicode.cldr.util.SupplementalDataInfo; 16 import org.unicode.cldr.util.SupplementalDataInfo.BasicLanguageData; 17 import org.unicode.cldr.util.SupplementalDataInfo.BasicLanguageData.Type; 18 import org.unicode.cldr.util.SupplementalDataInfo.CurrencyDateInfo; 19 import org.unicode.cldr.util.SupplementalDataInfo.PopulationData; 20 21 import com.ibm.icu.impl.Row; 22 import com.ibm.icu.impl.Row.R2; 23 24 public class LikelySubtags { 25 static final boolean DEBUG = true; 26 static final String TAG_SEPARATOR = "_"; 27 28 private Map<String, String> toMaximized; 29 private boolean favorRegion = false; 30 private static SupplementalDataInfo supplementalDataInfo; 31 private static Map<String, String> currencyToLikelyTerritory; 32 private static final Object SYNC = new Object(); 33 34 /** 35 * Create the likely subtags. 36 * 37 * @param toMaximized 38 */ LikelySubtags(Map<String, String> toMaximized)39 public LikelySubtags(Map<String, String> toMaximized) { 40 loadStaticVariables(); 41 if (this.toMaximized == null) { 42 this.toMaximized = supplementalDataInfo.getLikelySubtags(); 43 } else { 44 this.toMaximized = toMaximized; 45 } 46 } 47 loadStaticVariables()48 private static void loadStaticVariables() { 49 if (supplementalDataInfo != null && currencyToLikelyTerritory != null) { 50 return; 51 } 52 synchronized(SYNC) { 53 supplementalDataInfo = SupplementalDataInfo.getInstance(); 54 currencyToLikelyTerritory = new HashMap<>(); 55 Date now = new Date(); 56 Set<Row.R2<Double, String>> sorted = new TreeSet<>(); 57 for (String territory : supplementalDataInfo.getTerritoriesWithPopulationData()) { 58 PopulationData pop = supplementalDataInfo.getPopulationDataForTerritory(territory); 59 double population = pop.getPopulation(); 60 sorted.add(Row.of(-population, territory)); 61 } 62 for (R2<Double, String> item : sorted) { 63 String territory = item.get1(); 64 Set<CurrencyDateInfo> targetCurrencyInfo = supplementalDataInfo.getCurrencyDateInfo(territory); 65 if (targetCurrencyInfo == null) { 66 continue; 67 } 68 for (CurrencyDateInfo cdi : targetCurrencyInfo) { 69 String currency = cdi.getCurrency(); 70 if (!currencyToLikelyTerritory.containsKey(currency) && cdi.getStart().before(now) 71 && cdi.getEnd().after(now) && cdi.isLegalTender()) { 72 currencyToLikelyTerritory.put(currency, territory); 73 } 74 } 75 } 76 } 77 } 78 79 /** 80 * Create the likely subtags. 81 * 82 * @param toMaximized 83 */ LikelySubtags()84 public LikelySubtags() { 85 this(null); 86 } 87 isFavorRegion()88 public boolean isFavorRegion() { 89 return favorRegion; 90 } 91 setFavorRegion(boolean favorRegion)92 public LikelySubtags setFavorRegion(boolean favorRegion) { 93 this.favorRegion = favorRegion; 94 return this; 95 } 96 getToMaximized()97 public Map<String, String> getToMaximized() { 98 return toMaximized; 99 } 100 setToMaximized(Map<String, String> toMaximized)101 public LikelySubtags setToMaximized(Map<String, String> toMaximized) { 102 this.toMaximized = toMaximized; 103 return this; 104 } 105 maximize(String languageTag, Map<String, String> toMaximized)106 public static String maximize(String languageTag, Map<String, String> toMaximized) { 107 return new LikelySubtags(toMaximized).maximize(languageTag); 108 } 109 minimize(String input, Map<String, String> toMaximized, boolean favorRegion)110 public static String minimize(String input, Map<String, String> toMaximized, boolean favorRegion) { 111 return new LikelySubtags(toMaximized).setFavorRegion(favorRegion).minimize(input); 112 } 113 114 // TODO Old, crufty code, needs reworking. maximize(String languageTag)115 public synchronized String maximize(String languageTag) { 116 if (languageTag == null) { 117 return null; 118 } 119 LanguageTagParser ltp = new LanguageTagParser(); 120 if (DEBUG && languageTag.equals("es" + TAG_SEPARATOR + "Hans" + TAG_SEPARATOR + "CN")) { 121 System.out.print(""); // debug 122 } 123 // clean up the input by removing Zzzz, ZZ, and changing "" into und. 124 ltp.set(languageTag); 125 return maximize(ltp); 126 } 127 maximize(LanguageTagParser ltp)128 private String maximize(LanguageTagParser ltp) { 129 String language = ltp.getLanguage(); 130 String region = ltp.getRegion(); 131 String script = ltp.getScript(); 132 List<String> variants = ltp.getVariants(); 133 Map<String, String> extensions = ltp.getExtensions(); 134 Map<String, String> localeExtensions = ltp.getLocaleExtensions(); 135 136 if (language.equals("")) { 137 ltp.setLanguage(language = "und"); 138 } 139 if (script.equals("Zzzz")) { 140 ltp.setScript(script = ""); 141 } 142 if (region.equals("ZZ")) { 143 ltp.setRegion(region = ""); 144 } 145 if (variants.size() != 0) { 146 ltp.setVariants(Collections.<String> emptySet()); 147 } 148 if (extensions.size() != 0) { 149 ltp.setExtensions(Collections.<String, String> emptyMap()); 150 } 151 if (localeExtensions.size() != 0) { 152 ltp.setExtensions(Collections.<String, String> emptyMap()); 153 } 154 155 // check whole 156 String result = toMaximized.get(ltp.toString()); 157 if (result != null) { 158 return ltp.set(result) 159 .setVariants(variants) 160 .setExtensions(extensions) 161 .setLocaleExtensions(localeExtensions) 162 .toString(); 163 } 164 165 boolean noLanguage = language.equals("und"); 166 boolean noScript = script.isEmpty(); 167 boolean noRegion = region.isEmpty(); 168 169 // not efficient, but simple to match spec. 170 for (String region2 : noRegion ? Arrays.asList(region) : Arrays.asList(region, "")) { 171 ltp.setRegion(region2); 172 for (String script2 : noScript ? Arrays.asList(script) : Arrays.asList(script, "")) { 173 ltp.setScript(script2); 174 175 result = toMaximized.get(ltp.toString()); 176 if (result != null) { 177 ltp.set(result); 178 if (!noLanguage) { 179 ltp.setLanguage(language); 180 } 181 if (!noScript) { 182 ltp.setScript(script); 183 } 184 if (!noRegion) { 185 ltp.setRegion(region); 186 } 187 return ltp.setVariants(variants) 188 .setExtensions(extensions) 189 .setLocaleExtensions(localeExtensions) 190 .toString(); 191 } 192 } 193 } 194 195 // now check und_script 196 if (!noScript) { 197 ltp.setLanguage("und"); 198 ltp.setScript(script); 199 result = toMaximized.get(ltp.toString()); 200 if (result != null) { 201 ltp.set(result); 202 if (!noLanguage) { 203 ltp.setLanguage(language); 204 } 205 if (!noScript) { 206 ltp.setScript(script); 207 } 208 if (!noRegion) { 209 ltp.setRegion(region); 210 } 211 return ltp.setVariants(variants) 212 .setExtensions(extensions) 213 .setLocaleExtensions(localeExtensions) 214 .toString(); 215 } 216 } 217 218 return null; // couldn't maximize 219 } 220 221 // TODO, optimize if needed by adding private routine that maximizes a LanguageTagParser instead of multiple parsings 222 // TODO Old, crufty code, needs reworking. minimize(String input)223 public String minimize(String input) { 224 return minimize(input, OutputOption.ICU_LCVARIANT); 225 } 226 minimize(String input, OutputOption oo)227 public synchronized String minimize(String input, OutputOption oo) { 228 String maximized = maximize(input, toMaximized); 229 if (maximized == null) { 230 return null; 231 } 232 if (DEBUG && maximized.equals("sr" + TAG_SEPARATOR + "Latn" + TAG_SEPARATOR + "RS")) { 233 System.out.print(""); // debug 234 } 235 LanguageTagParser ltp = new LanguageTagParser().set(maximized); 236 String language = ltp.getLanguage(); 237 String region = ltp.getRegion(); 238 String script = ltp.getScript(); 239 240 // handle variants 241 List<String> variants = ltp.getVariants(); 242 Map<String, String> extensions = ltp.getExtensions(); 243 Map<String, String> localeExtensions = ltp.getLocaleExtensions(); 244 245 String maximizedCheck = maximized; 246 if (!variants.isEmpty() || !extensions.isEmpty() || !localeExtensions.isEmpty()) { 247 maximizedCheck = ltp.toLSR(); 248 } 249 // try building up from shorter to longer, and find the first that matches 250 // could be more optimized, but for this code we want simplest 251 String[] trials = { language, 252 language + TAG_SEPARATOR + (favorRegion ? region : script), 253 language + TAG_SEPARATOR + (!favorRegion ? region : script) }; 254 for (String trial : trials) { 255 String newMaximized = maximize(trial, toMaximized); 256 if (maximizedCheck.equals(newMaximized)) { 257 if (variants.isEmpty() && extensions.isEmpty() && localeExtensions.isEmpty()) { 258 return trial; 259 } 260 return ltp.set(trial) 261 .setVariants(variants) 262 .setExtensions(extensions) 263 .setLocaleExtensions(extensions) 264 .toString(oo); 265 } 266 } 267 return maximized; 268 } 269 270 static final Map<String, String> EXTRA_SCRIPTS = Builder.with(new HashMap<String, String>()) 271 .on("crs", "pcm", "tlh").put("Latn") 272 .freeze(); 273 getLikelyScript(String code)274 public String getLikelyScript(String code) { 275 String max = this.maximize(code); 276 277 String script = null; 278 if (max != null) { 279 script = new LanguageTagParser().set(max).getScript(); 280 } else { 281 Map<Type, BasicLanguageData> data = supplementalDataInfo.getBasicLanguageDataMap(code); 282 if (data != null) { 283 for (BasicLanguageData item : data.values()) { 284 Set<String> scripts = item.getScripts(); 285 if (scripts == null || scripts.size() == 0) continue; 286 script = scripts.iterator().next(); 287 Type type = item.getType(); 288 if (type == Type.primary) { 289 break; 290 } 291 } 292 } 293 if (script == null) { 294 script = EXTRA_SCRIPTS.get(code); 295 if (script == null) { 296 script = "Zzzz"; 297 } 298 } 299 } 300 return script; 301 } 302 getLikelyTerritoryFromCurrency(String code)303 public String getLikelyTerritoryFromCurrency(String code) { 304 return currencyToLikelyTerritory.get(code); 305 } 306 } 307