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