1 /* 2 ********************************************************************** 3 * Copyright (c) 2002-2011, International Business Machines 4 * Corporation and others. All Rights Reserved. 5 ********************************************************************** 6 * Author: Mark Davis 7 ********************************************************************** 8 */ 9 package org.unicode.cldr.util; 10 11 import java.io.BufferedReader; 12 import java.util.ArrayList; 13 import java.util.Arrays; 14 import java.util.Collections; 15 import java.util.Comparator; 16 import java.util.EnumMap; 17 import java.util.EnumSet; 18 import java.util.HashMap; 19 import java.util.Iterator; 20 import java.util.LinkedHashMap; 21 import java.util.LinkedHashSet; 22 import java.util.List; 23 import java.util.Locale; 24 import java.util.Map; 25 import java.util.Map.Entry; 26 import java.util.Set; 27 import java.util.TreeMap; 28 import java.util.TreeSet; 29 import java.util.regex.Pattern; 30 31 import org.unicode.cldr.draft.ScriptMetadata; 32 import org.unicode.cldr.draft.ScriptMetadata.IdUsage; 33 import org.unicode.cldr.util.Iso639Data.Type; 34 import org.unicode.cldr.util.ZoneParser.ZoneLine; 35 36 import com.ibm.icu.impl.Relation; 37 import com.ibm.icu.lang.UCharacter; 38 import com.ibm.icu.text.UnicodeSet; 39 import com.ibm.icu.util.Output; 40 41 /** 42 * Provides access to various codes used by CLDR: RFC 3066, ISO 4217, Olson 43 * tzids 44 */ 45 public class StandardCodes { 46 47 public enum CodeType { 48 language, script, territory, extlang, legacy, redundant, variant, currency, tzid; from(String name)49 public static CodeType from(String name) { 50 if ("region".equals(name)) { 51 return territory; 52 } 53 return CodeType.valueOf(name); 54 } 55 } 56 57 private static final Set<CodeType> TypeSet = Collections.unmodifiableSet(EnumSet.allOf(CodeType.class)); 58 59 private static final Set<String> TypeStringSet; 60 static { 61 LinkedHashSet<String> foo = new LinkedHashSet<>(); 62 for (CodeType x : CodeType.values()) { x.toString()63 foo.add(x.toString()); 64 } 65 TypeStringSet = Collections.unmodifiableSet(foo); 66 } 67 68 public static final String DESCRIPTION_SEPARATOR = "\u25AA"; 69 70 public static final String NO_COUNTRY = "001"; 71 72 private EnumMap<CodeType, Map<String, List<String>>> type_code_data = new EnumMap<>( 73 CodeType.class); 74 75 private EnumMap<CodeType, Map<String, List<String>>> type_name_codes = new EnumMap<>( 76 CodeType.class); 77 78 private EnumMap<CodeType, Map<String, String>> type_code_preferred = new EnumMap<>( 79 CodeType.class); 80 81 private Map<String, Set<String>> country_modernCurrency = new TreeMap<>(); 82 83 private Map<CodeType, Set<String>> goodCodes = new TreeMap<>(); 84 85 private static final boolean DEBUG = false; 86 87 private static final class StandardCodesHelper { 88 static final StandardCodes SINGLETON = new StandardCodes(); 89 } 90 /** 91 * Get the singleton copy of the standard codes. 92 */ make()93 static public synchronized StandardCodes make() { 94 return StandardCodesHelper.SINGLETON; 95 } 96 97 /** 98 * The data is the name in the case of RFC3066 codes, and the country code in 99 * the case of TZIDs and ISO currency codes. If the country code is missing, 100 * uses ZZ. 101 */ getData(String type, String code)102 public String getData(String type, String code) { 103 Map<String, List<String>> code_data = getCodeData(type); 104 if (code_data == null) 105 return null; 106 List<String> list = code_data.get(code); 107 if (list == null) 108 return null; 109 return list.get(0); 110 } 111 112 /** 113 * @return the full data for the type and code For the data in lstreg, it is 114 * description | date | canonical_value | recommended_prefix # 115 * comments 116 */ getFullData(String type, String code)117 public List<String> getFullData(String type, String code) { 118 Map<String, List<String>> code_data = getCodeData(type); 119 if (code_data == null) 120 return null; 121 return code_data.get(code); 122 } 123 124 /** 125 * @return the full data for the type and code For the data in lstreg, it is 126 * description | date | canonical_value | recommended_prefix # 127 * comments 128 */ getFullData(CodeType type, String code)129 public List<String> getFullData(CodeType type, String code) { 130 Map<String, List<String>> code_data = type_code_data.get(type); 131 if (code_data == null) 132 return null; 133 return code_data.get(code); 134 } 135 getCodeData(String type)136 private Map<String, List<String>> getCodeData(String type) { 137 return getCodeData(CodeType.from(type)); 138 } 139 getCodeData(CodeType type)140 private Map<String, List<String>> getCodeData(CodeType type) { 141 return type_code_data.get(type); 142 } 143 144 /** 145 * Get at the language registry values, as a Map from label to value. 146 * 147 * @param type 148 * @param code 149 * @return 150 */ getLangData(String type, String code)151 public Map<String, String> getLangData(String type, String code) { 152 try { 153 if (type.equals("territory")) 154 type = "region"; 155 else if (type.equals("variant")) code = code.toLowerCase(Locale.ENGLISH); 156 return (Map) ((Map) getLStreg().get(type)).get(code); 157 } catch (RuntimeException e) { 158 return null; 159 } 160 } 161 162 /** 163 * Return a replacement code, if available. If not, return null. 164 * 165 */ getReplacement(String type, String code)166 public String getReplacement(String type, String code) { 167 if (type.equals("currency")) 168 return null; // no replacement codes for currencies 169 List<String> data = getFullData(type, code); 170 if (data == null) 171 return null; 172 // if available, the replacement is a non-empty value other than --, in 173 // position 2. 174 if (data.size() < 3) 175 return null; 176 String replacement = data.get(2); 177 if (!replacement.equals("") && !replacement.equals("--")) 178 return replacement; 179 return null; 180 } 181 182 /** 183 * Return the list of codes that have the same data. For example, returns all 184 * currency codes for a country. If there is a preferred one, it is first. 185 * 186 * @param type 187 * @param data 188 * @return 189 */ 190 @Deprecated getCodes(String type, String data)191 public List<String> getCodes(String type, String data) { 192 return getCodes(CodeType.from(type), data); 193 } 194 195 /** 196 * Return the list of codes that have the same data. For example, returns all 197 * currency codes for a country. If there is a preferred one, it is first. 198 */ getCodes(CodeType type, String data)199 public List<String> getCodes(CodeType type, String data) { 200 Map<String, List<String>> data_codes = type_name_codes.get(type); 201 if (data_codes == null) 202 return null; 203 return Collections.unmodifiableList(data_codes.get(data)); 204 } 205 206 /** 207 * Where there is a preferred code, return it. 208 */ 209 @Deprecated getPreferred(String type, String code)210 public String getPreferred(String type, String code) { 211 return getPreferred(CodeType.from(type), code); 212 } 213 214 /** 215 * Where there is a preferred code, return it. 216 */ 217 getPreferred(CodeType type, String code)218 public String getPreferred(CodeType type, String code) { 219 Map<String, String> code_preferred = type_code_preferred.get(type); 220 if (code_preferred == null) 221 return code; 222 String newCode = code_preferred.get(code); 223 if (newCode == null) 224 return code; 225 return newCode; 226 } 227 228 /** 229 * Get all the available types 230 */ getAvailableTypes()231 public Set<String> getAvailableTypes() { 232 return TypeStringSet; 233 } 234 235 /** 236 * Get all the available types 237 */ getAvailableTypesEnum()238 public Set<CodeType> getAvailableTypesEnum() { 239 return TypeSet; 240 } 241 242 /** 243 * Get all the available codes for a given type 244 * 245 * @param type 246 * @return 247 */ getAvailableCodes(String type)248 public Set<String> getAvailableCodes(String type) { 249 return getAvailableCodes(CodeType.from(type)); 250 } 251 252 /** 253 * Get all the available codes for a given type 254 * 255 * @param type 256 * @return 257 */ getAvailableCodes(CodeType type)258 public Set<String> getAvailableCodes(CodeType type) { 259 Map<String, List<String>> code_name = type_code_data.get(type); 260 return Collections.unmodifiableSet(code_name.keySet()); 261 } 262 getGoodAvailableCodes(String stringType)263 public Set<String> getGoodAvailableCodes(String stringType) { 264 return getGoodAvailableCodes(CodeType.from(stringType)); 265 } 266 267 /** 268 * Get all the available "real" codes for a given type, excluding private use, 269 * but including some deprecated codes. Use SupplementalDataInfo getLocaleAliases to 270 * exclude others. 271 * 272 * @param type 273 * @return 274 */ getGoodAvailableCodes(CodeType type)275 public Set<String> getGoodAvailableCodes(CodeType type) { 276 Set<String> result = goodCodes.get(type); 277 if (result == null) { 278 synchronized (goodCodes) { 279 Map<String, List<String>> code_name = getCodeData(type); 280 SupplementalDataInfo sd = SupplementalDataInfo.getInstance(); 281 if (code_name == null) 282 return null; 283 result = new TreeSet<>(code_name.keySet()); 284 switch (type) { 285 case currency: 286 break; // nothing special 287 case language: 288 return sd.getCLDRLanguageCodes(); 289 case script: 290 return sd.getCLDRScriptCodes(); 291 case tzid: 292 break; // nothing special 293 default: 294 for (Iterator<String> it = result.iterator(); it.hasNext();) { 295 String code = it.next(); 296 if (code.equals("root") || code.equals("QO")) 297 continue; 298 List<String> data = getFullData(type, code); 299 if (data.size() < 3) { 300 if (DEBUG) 301 System.out.println(code + "\t" + data); 302 } 303 if ("PRIVATE USE".equalsIgnoreCase(data.get(0)) 304 || (!data.get(2).equals("") && !data.get(2).equals("--"))) { 305 // System.out.println("Removing: " + code); 306 it.remove(); 307 } 308 } 309 } 310 result = Collections.unmodifiableSet(result); 311 goodCodes.put(type, result); 312 } 313 } 314 return result; 315 } 316 317 private static Set<String> GOOD_COUNTRIES; 318 getGoodCountries()319 public Set<String> getGoodCountries() { 320 synchronized (goodCodes) { 321 if (GOOD_COUNTRIES == null) { 322 Set<String> temp = new LinkedHashSet<>(); 323 for (String s : getGoodAvailableCodes(CodeType.territory)) { 324 if (isCountry(s)) { 325 temp.add(s); 326 } 327 } 328 GOOD_COUNTRIES = Collections.unmodifiableSet(temp); 329 } 330 } 331 return GOOD_COUNTRIES; 332 } 333 334 /** 335 * Gets the modern currency. 336 */ getMainCurrencies(String countryCode)337 public Set<String> getMainCurrencies(String countryCode) { 338 return country_modernCurrency.get(countryCode); 339 } 340 341 // /** 342 // * Get rid of this 343 // * 344 // * @param type 345 // * @return 346 // * @throws IOException 347 // * @deprecated 348 // */ 349 // public String getEffectiveLocaleType(String type) throws IOException { 350 // if ((type != null) && (getLocaleCoverageOrganizations().contains(Organization.valueOf(type)))) { 351 // return type; 352 // } else { 353 // return null; // the default.. for now.. 354 // } 355 // } 356 357 static Comparator caseless = new Comparator() { 358 359 @Override 360 public int compare(Object arg0, Object arg1) { 361 String s1 = (String) arg0; 362 String s2 = (String) arg1; 363 return s1.compareToIgnoreCase(s2); 364 } 365 366 }; 367 368 /** 369 * Used for Locales.txt to mean "all" 370 */ 371 public static final String ALL_LOCALES = "*"; 372 373 /** 374 * Returns locales according to status. It returns a Map of Maps, key 1 is 375 * either IBM or Java (perhaps more later), key 2 is the Level. 376 * 377 * @deprecated 378 */ 379 @Deprecated getLocaleTypes()380 public Map<Organization, Map<String, Level>> getLocaleTypes() { 381 synchronized (StandardCodes.class) { 382 return loadPlatformLocaleStatus().platform_locale_level; 383 } 384 } 385 386 /** 387 * Return map of locales to levels 388 * @param org 389 * @return 390 */ getLocaleToLevel(Organization org)391 public Map<String, Level> getLocaleToLevel(Organization org) { 392 return getLocaleTypes().get(org); 393 } 394 395 /** 396 * returns the highest level in the hierarchy, not including root. 397 */ getHighestLocaleCoverageLevel(String organization, String locale)398 public Level getHighestLocaleCoverageLevel(String organization, String locale) { 399 // first get parent 400 final String parentId = LocaleIDParser.getParent(locale); 401 Level parentLevel = Level.UNDETERMINED; 402 if (parentId != null && !parentId.equals("root")) { 403 parentLevel = getHighestLocaleCoverageLevel(organization, parentId); // recurse 404 } 405 final Level ourLevel = getLocaleCoverageLevel(organization, locale); 406 if (parentLevel.getLevel() > ourLevel.getLevel()) { 407 // if parentLevel is higher 408 return parentLevel; 409 } else { 410 return ourLevel; 411 } 412 } 413 getLocaleCoverageLevel(String organization, String desiredLocale)414 public Level getLocaleCoverageLevel(String organization, String desiredLocale) { 415 return getLocaleCoverageLevel(Organization.fromString(organization), desiredLocale); 416 } 417 getLocaleCoverageLevel(Organization organization, String desiredLocale)418 public Level getLocaleCoverageLevel(Organization organization, String desiredLocale) { 419 return getLocaleCoverageLevel(organization, desiredLocale, new Output<LocaleCoverageType>()); 420 } 421 422 public enum LocaleCoverageType { 423 explicit, parent, star, undetermined 424 } 425 426 /** 427 * Returns coverage level of locale according to organization. Returns Level.UNDETERMINED if information is missing. 428 * A locale of "*" in the data means "everything else". 429 */ getLocaleCoverageLevel(Organization organization, String desiredLocale, Output<LocaleCoverageType> coverageType)430 public Level getLocaleCoverageLevel(Organization organization, String desiredLocale, Output<LocaleCoverageType> coverageType) { 431 coverageType.value = LocaleCoverageType.undetermined; 432 if (organization == null) { 433 return Level.UNDETERMINED; 434 } 435 Map<String, Level> locale_status = loadPlatformLocaleStatus().platform_locale_level.get(organization); 436 if (locale_status == null) { 437 return Level.UNDETERMINED; 438 } 439 // see if there is a parent 440 String originalLocale = desiredLocale; 441 while (desiredLocale != null) { 442 Level status = locale_status.get(desiredLocale); 443 if (status != null && status != Level.UNDETERMINED) { 444 coverageType.value = originalLocale == desiredLocale ? LocaleCoverageType.explicit : LocaleCoverageType.parent; 445 return status; 446 } 447 desiredLocale = LocaleIDParser.getParent(desiredLocale); 448 } 449 Level status = locale_status.get(ALL_LOCALES); 450 if (status != null && status != Level.UNDETERMINED) { 451 coverageType.value = LocaleCoverageType.star; 452 return status; 453 } 454 return Level.UNDETERMINED; 455 } 456 457 /** 458 * Returns coverage level of locale according to organization. Returns Level.UNDETERMINED if information is missing. 459 */ getDefaultLocaleCoverageLevel(Organization organization)460 public Level getDefaultLocaleCoverageLevel(Organization organization) { 461 return getLocaleCoverageLevel(organization, ALL_LOCALES); 462 } 463 getLocaleCoverageOrganizations()464 public Set<Organization> getLocaleCoverageOrganizations() { 465 return loadPlatformLocaleStatus().platform_locale_level.keySet(); 466 } 467 getLocaleCoverageOrganizationStrings()468 public Set<String> getLocaleCoverageOrganizationStrings() { 469 return loadPlatformLocaleStatus().platform_locale_levelString.keySet(); 470 } 471 getLocaleCoverageLocales(String organization)472 public Set<String> getLocaleCoverageLocales(String organization) { 473 return getLocaleCoverageLocales(Organization.fromString(organization)); 474 } 475 getLocaleCoverageLocales(Organization organization)476 public Set<String> getLocaleCoverageLocales(Organization organization) { 477 return loadPlatformLocaleStatus().platform_locale_level.get(organization).keySet(); 478 } 479 getLocalesToLevelsFor(Organization organization)480 public Map<String, Level> getLocalesToLevelsFor(Organization organization) { 481 return loadPlatformLocaleStatus().platform_locale_level.get(organization); 482 } 483 getLevelsToLocalesFor(Organization organization)484 public Relation<Level, String> getLevelsToLocalesFor(Organization organization) { 485 return loadPlatformLocaleStatus().platform_level_locale.get(organization); 486 } 487 getLocaleCoverageLocales(Organization organization, Set<Level> choice)488 public Set<String> getLocaleCoverageLocales(Organization organization, Set<Level> choice) { 489 Set<String> result = new LinkedHashSet<>(); 490 for (String locale : getLocaleCoverageLocales(organization)) { 491 if (choice.contains(getLocaleCoverageLevel(organization, locale))) { 492 result.add(locale); 493 } 494 } 495 return result; 496 } 497 498 /** 499 * "The target coverage level is set to: 500 * - The CLDR Org coverage level if it exists, 501 * - Otherise, the maximum of all the coverage levels for that locale across all Organizations 502 * (max Modern) in Locales.txt, if there is at least one. 503 * - Otherwise Basic. 504 * - That makes the number the same for all Organizations, which makes communicating the values less prone 505 * to misinterpretation, and gives all the vetters and managers a common metric for that locale. 506 */ getTargetCoverageLevel(String localeId)507 public Level getTargetCoverageLevel(String localeId) { 508 Level level; 509 510 // First, try CLDR locale 511 level = getLocaleCoverageLevel(Organization.cldr, localeId); 512 if (level != Level.UNDETERMINED) { 513 return level; 514 } 515 516 // Next, Find maximum coverage level 517 for (final Organization o : Organization.values()) { 518 if (o == Organization.cldr || // Already handled, above 519 o == Organization.guest || 520 o == Organization.surveytool) { 521 continue; // Skip some 'special' orgs 522 } 523 final Output<StandardCodes.LocaleCoverageType> outputType = new Output<>(); 524 final Level orgLevel = getLocaleCoverageLevel(o, localeId, outputType); 525 if (outputType.value == StandardCodes.LocaleCoverageType.undetermined || 526 outputType.value == StandardCodes.LocaleCoverageType.star) { 527 // Skip undetermined or star 528 continue; 529 } 530 // Pin the level to MODERN 531 final Level pinnedOrgLevel = Level.min(Level.MODERN, orgLevel); 532 // Accumulate the maxiumum org level (up to MODERN) 533 level = Level.max(level, pinnedOrgLevel); 534 } 535 if (level != Level.UNDETERMINED) { 536 return level; 537 } 538 539 // Otherwise, BASIC 540 level = Level.BASIC; 541 return level; 542 } 543 544 private static final class LocalesTxtHelper { 545 static LocalesTxtHelper SINGLETON = new LocalesTxtHelper(); 546 547 public LocalesTxtReader reader; 548 LocalesTxtHelper()549 LocalesTxtHelper() { 550 reader = new LocalesTxtReader() 551 .read(StandardCodes.make()); // circular dependency 552 } 553 } 554 555 /** 556 * Get the 'platform locale status' (aka Locales.txt) 557 * Note, do not call this from the StandardCodes constructor! 558 * @return 559 */ loadPlatformLocaleStatus()560 private LocalesTxtReader loadPlatformLocaleStatus() { 561 return LocalesTxtHelper.SINGLETON.reader; 562 } 563 validate(LocaleIDParser parser)564 String validate(LocaleIDParser parser) { 565 String message = ""; 566 String lang = parser.getLanguage(); 567 if (lang.length() == 0) { 568 message += ", Missing language"; 569 } else if (!getAvailableCodes("language").contains(lang)) { 570 message += ", Invalid language code: " + lang; 571 } 572 String script = parser.getScript(); 573 if (script.length() != 0 && !getAvailableCodes("script").contains(script)) { 574 message += ", Invalid script code: " + script; 575 } 576 String territory = parser.getRegion(); 577 if (territory.length() != 0 && !getAvailableCodes("territory").contains(territory)) { 578 message += ", Invalid territory code: " + lang; 579 } 580 return message.length() == 0 ? message : message.substring(2); 581 } 582 583 /** 584 * Ascertain that the given locale in in the given group specified by the 585 * organization 586 * 587 * @param locale 588 * @param group 589 * @param org 590 * @return boolean 591 */ isLocaleInGroup(String locale, String group, Organization org)592 public boolean isLocaleInGroup(String locale, String group, Organization org) { 593 return group.equals(getGroup(locale, org)); 594 } 595 isLocaleInGroup(String locale, String group, String org)596 public boolean isLocaleInGroup(String locale, String group, String org) { 597 return isLocaleInGroup(locale, group, Organization.fromString(org)); 598 } 599 getGroup(String locale, String org)600 public String getGroup(String locale, String org) { 601 return getGroup(locale, Organization.fromString(org)); 602 } 603 604 /** 605 * Gets the coverage group given a locale and org 606 * 607 * @param locale 608 * @param org 609 * @return group if availble, null if not 610 */ getGroup(String locale, Organization org)611 private String getGroup(String locale, Organization org) { 612 Level l = getLocaleCoverageLevel(org, locale); 613 if (l.equals(Level.UNDETERMINED)) { 614 return null; 615 } else { 616 return l.toString(); 617 } 618 } 619 620 // ========== PRIVATES ========== 621 StandardCodes()622 private StandardCodes() { 623 String[] files = { "ISO4217.txt" }; // , "TZID.txt" 624 type_code_preferred.put(CodeType.tzid, new TreeMap<String, String>()); 625 add(CodeType.language, "root", "Root"); 626 String originalLine = null; 627 for (int fileIndex = 0; fileIndex < files.length; ++fileIndex) { 628 try { 629 BufferedReader lstreg = CldrUtility.getUTF8Data(files[fileIndex]); 630 while (true) { 631 String line = originalLine = lstreg.readLine(); 632 if (line == null) 633 break; 634 if (line.startsWith("\uFEFF")) { 635 line = line.substring(1); 636 } 637 line = line.trim(); 638 int commentPos = line.indexOf('#'); 639 String comment = ""; 640 if (commentPos >= 0) { 641 comment = line.substring(commentPos + 1).trim(); 642 line = line.substring(0, commentPos); 643 } 644 if (line.length() == 0) 645 continue; 646 List<String> pieces = CldrUtility.splitList(line, '|', true, 647 new ArrayList<String>()); 648 CodeType type = CodeType.from(pieces.get(0)); 649 pieces.remove(0); 650 651 String code = pieces.get(0); 652 pieces.remove(0); 653 if (type.equals("date")) { 654 continue; 655 } 656 657 String oldName = pieces.get(0); 658 int pos = oldName.indexOf(';'); 659 if (pos >= 0) { 660 oldName = oldName.substring(0, pos).trim(); 661 pieces.set(0, oldName); 662 } 663 664 List<String> data = pieces; 665 if (comment.indexOf("deprecated") >= 0) { 666 // System.out.println(originalLine); 667 if (data.get(2).toString().length() == 0) { 668 data.set(2, "--"); 669 } 670 } 671 if (oldName.equalsIgnoreCase("PRIVATE USE")) { 672 int separatorPos = code.indexOf(".."); 673 if (separatorPos < 0) { 674 add(type, code, data); 675 } else { 676 String current = code.substring(0, separatorPos); 677 String end = code.substring(separatorPos + 2); 678 // System.out.println(">>" + code + "\t" + current + "\t" + end); 679 for (; current.compareTo(end) <= 0; current = nextAlpha(current)) { 680 // System.out.println(">" + current); 681 add(type, current, data); 682 } 683 } 684 continue; 685 } 686 if (!type.equals("tzid")) { 687 add(type, code, data); 688 if (type.equals("currency")) { 689 // currency | TPE | Timor Escudo | TP | EAST TIMOR | O 690 if (data.get(3).equals("C")) { 691 String country = data.get(1); 692 Set<String> codes = country_modernCurrency.get(country); 693 if (codes == null) { 694 country_modernCurrency.put(country, codes = new TreeSet<>()); 695 } 696 codes.add(code); 697 } 698 } 699 continue; 700 } 701 // type = tzid 702 // List codes = (List) Utility.splitList(code, ',', true, new 703 // ArrayList()); 704 String preferred = null; 705 for (int i = 0; i < pieces.size(); ++i) { 706 code = pieces.get(i); 707 add(type, code, data); 708 if (preferred == null) 709 preferred = code; 710 else { 711 Map<String, String> code_preferred = type_code_preferred.get(type); 712 code_preferred.put(code, preferred); 713 } 714 } 715 } 716 lstreg.close(); 717 } catch (Exception e) { 718 System.err.println("WARNING: " + files[fileIndex] 719 + " may be a corrupted UTF-8 file. Please check."); 720 throw (IllegalArgumentException) new IllegalArgumentException( 721 "Can't read " + files[fileIndex] + "\t" + originalLine) 722 .initCause(e); 723 } 724 country_modernCurrency = CldrUtility.protectCollection(country_modernCurrency); 725 } 726 727 // data is: description | date | canonical_value | recommended_prefix # 728 // comments 729 // HACK, just rework 730 731 Map<String, Map<String, Map<String, String>>> languageRegistry = getLStreg(); 732 // languageRegistry = CldrUtility.protectCollection(languageRegistry); 733 734 for (String type : languageRegistry.keySet()) { 735 CodeType type2 = CodeType.from(type); 736 Map<String, Map<String, String>> m = languageRegistry.get(type); 737 for (String code : m.keySet()) { 738 Map<String, String> mm = m.get(code); 739 List<String> data = new ArrayList<>(0); 740 data.add(mm.get("Description")); 741 data.add(mm.get("Added")); 742 String pref = mm.get("Preferred-Value"); 743 if (pref == null) { 744 pref = mm.get("Deprecated"); 745 if (pref == null) 746 pref = ""; 747 else 748 pref = "deprecated"; 749 } 750 data.add(pref); 751 if (type.equals("variant")) { 752 code = code.toUpperCase(); 753 } 754 // data.add(mm.get("Recommended_Prefix")); 755 // {"region", "BQ", "Description", "British Antarctic Territory", 756 // "Preferred-Value", "AQ", "CLDR", "True", "Deprecated", "True"}, 757 add(type2, code, data); 758 } 759 } 760 761 Map<String, List<String>> m = getZoneData(); 762 for (Iterator<String> it = m.keySet().iterator(); it.hasNext();) { 763 String code = it.next(); 764 add(CodeType.tzid, code, m.get(code).toString()); 765 } 766 } 767 768 /** 769 * @param current 770 * @return 771 */ nextAlpha(String current)772 private static String nextAlpha(String current) { 773 // Don't care that this is inefficient 774 int value = 0; 775 for (int i = 0; i < current.length(); ++i) { 776 char c = current.charAt(i); 777 c -= c < 'a' ? 'A' : 'a'; 778 value = value * 26 + c; 779 } 780 value += 1; 781 String result = ""; 782 for (int i = 0; i < current.length(); ++i) { 783 result = (char) ((value % 26) + 'A') + result; 784 value = value / 26; 785 } 786 if (UCharacter.toLowerCase(current).equals(current)) { 787 result = UCharacter.toLowerCase(result); 788 } else if (UCharacter.toUpperCase(current).equals(current)) { 789 // do nothing 790 } else { 791 result = UCharacter.toTitleCase(result, null); 792 } 793 return result; 794 } 795 796 /** 797 * @param string 798 * @param string2 799 * @param string3 800 */ 801 private void add(CodeType type, String string2, String string3) { 802 List<String> l = new ArrayList<>(); 803 l.add(string3); 804 add(type, string2, l); 805 } 806 807 private void add(CodeType type, String code, List<String> otherData) { 808 // hack 809 if (type == CodeType.script) { 810 if (code.equals("Qaai")) { 811 otherData = new ArrayList<>(otherData); 812 otherData.set(0, "Inherited"); 813 } else if (code.equals("Zyyy")) { 814 otherData = new ArrayList<>(otherData); 815 otherData.set(0, "Common"); 816 } 817 } 818 819 // assume name is the first item 820 821 String name = otherData.get(0); 822 823 // add to main list 824 Map<String, List<String>> code_data = getCodeData(type); 825 if (code_data == null) { 826 code_data = new TreeMap<>(); 827 type_code_data.put(type, code_data); 828 } 829 List<String> lastData = code_data.get(code); 830 if (lastData != null) { 831 lastData.addAll(otherData); 832 } else { 833 code_data.put(code, otherData); 834 } 835 836 // now add mapping from name to codes 837 Map<String, List<String>> name_codes = type_name_codes.get(type); 838 if (name_codes == null) { 839 name_codes = new TreeMap<>(); 840 type_name_codes.put(type, name_codes); 841 } 842 List<String> codes = name_codes.get(name); 843 if (codes == null) { 844 codes = new ArrayList<>(); 845 name_codes.put(name, codes); 846 } 847 codes.add(code); 848 } 849 850 private List<String> DELETED3166 = Collections.unmodifiableList(Arrays 851 .asList(new String[] { "BQ", "BU", "CT", "DD", "DY", "FQ", "FX", "HV", 852 "JT", "MI", "NH", "NQ", "NT", "PC", "PU", "PZ", "RH", "SU", "TP", 853 "VD", "WK", "YD", "YU", "ZR" })); 854 855 public List<String> getOld3166() { 856 return DELETED3166; 857 } 858 859 private Map<String, List<String>> WorldBankInfo; 860 861 public Map<String, List<String>> getWorldBankInfo() { 862 if (WorldBankInfo == null) { 863 List<String> temp = fillFromCommaFile("WorldBankInfo.txt", false); 864 WorldBankInfo = new HashMap<>(); 865 for (String line : temp) { 866 List<String> row = CldrUtility.splitList(line, ';', true); 867 String key = row.get(0); 868 row.remove(0); 869 WorldBankInfo.put(key, row); 870 } 871 WorldBankInfo = CldrUtility.protectCollection(WorldBankInfo); 872 } 873 return WorldBankInfo; 874 } 875 876 Set<String> moribundLanguages; 877 878 public Set<String> getMoribundLanguages() { 879 if (moribundLanguages == null) { 880 List<String> temp = fillFromCommaFile("moribund_languages.txt", true); 881 moribundLanguages = new TreeSet<>(); 882 moribundLanguages.addAll(temp); 883 moribundLanguages = CldrUtility.protectCollection(moribundLanguages); 884 } 885 return moribundLanguages; 886 } 887 888 // produces a list of the 'clean' lines 889 private List<String> fillFromCommaFile(String filename, boolean trim) { 890 try { 891 List<String> result = new ArrayList<>(); 892 String line; 893 BufferedReader lstreg = CldrUtility.getUTF8Data(filename); 894 while (true) { 895 line = lstreg.readLine(); 896 if (line == null) 897 break; 898 int commentPos = line.indexOf('#'); 899 if (commentPos >= 0) { 900 line = line.substring(0, commentPos); 901 } 902 if (trim) { 903 line = line.trim(); 904 } 905 if (line.length() == 0) 906 continue; 907 result.add(line); 908 } 909 return result; 910 } catch (Exception e) { 911 throw (RuntimeException) new IllegalArgumentException( 912 "Can't process file: data/" + filename).initCause(e); 913 } 914 } 915 916 // return a complex map. language -> arn -> {"Comments" -> "x", 917 // "Description->y,...} 918 static String[][] extras = { 919 { "language", "root", "Description", "Root", "CLDR", "True" }, 920 // { "language", "cch", "Description", "Atsam", "CLDR", "True" }, 921 // { "language", "kaj", "Description", "Jju", "CLDR", "True" }, 922 // { "language", "kcg", "Description", "Tyap", "CLDR", "True" }, 923 // { "language", "kfo", "Description", "Koro", "CLDR", "True" }, 924 // { "language", "mfe", "Description", "Morisyen", "CLDR", "True" }, 925 // { "region", "172", "Description", "Commonwealth of Independent States", "CLDR", "True" }, 926 // { "region", "062", "Description", "South-Central Asia", "CLDR", "True" }, 927 // { "region", "003", "Description", "North America", "CLDR", "True" }, 928 // { "variant", "POLYTONI", "Description", "Polytonic Greek", "CLDR", "True", "Preferred-Value", "POLYTON" }, 929 { "variant", "REVISED", "Description", "Revised Orthography", "CLDR", "True" }, 930 { "variant", "SAAHO", "Description", "Dialect", "CLDR", "True" }, 931 { "variant", "POSIX", "Description", "Computer-Style", "CLDR", "True" }, 932 // {"region", "172", "Description", "Commonwealth of Independent States", 933 // "CLDR", "True"}, 934 // { "region", "", "Description", "European Union", "CLDR", "True" }, 935 { "region", "ZZ", "Description", "Unknown or Invalid Region", "CLDR", "True" }, 936 { "region", "QO", "Description", "Outlying Oceania", "CLDR", "True" }, 937 { "region", "XK", "Description", "Kosovo", "CLDR", "True" }, 938 { "script", "Qaai", "Description", "Inherited", "CLDR", "True" }, 939 // {"region", "003", "Description", "North America", "CLDR", "True"}, 940 // {"region", "062", "Description", "South-central Asia", "CLDR", "True"}, 941 // {"region", "200", "Description", "Czechoslovakia", "CLDR", "True"}, 942 // {"region", "830", "Description", "Channel Islands", "CLDR", "True"}, 943 // {"region", "833", "Description", "Isle of Man", "CLDR", "True"}, 944 945 // {"region", "NT", "Description", "Neutral Zone (formerly between Saudi 946 // Arabia & Iraq)", "CLDR", "True", "Deprecated", "True"}, 947 // {"region", "SU", "Description", "Union of Soviet Socialist Republics", 948 // "CLDR", "True", "Deprecated", "True"}, 949 // {"region", "BQ", "Description", "British Antarctic Territory", 950 // "Preferred-Value", "AQ", "CLDR", "True", "Deprecated", "True"}, 951 // {"region", "CT", "Description", "Canton and Enderbury Islands", 952 // "Preferred-Value", "KI", "CLDR", "True", "Deprecated", "True"}, 953 // {"region", "FQ", "Description", "French Southern and Antarctic Territories 954 // (now split between AQ and TF)", "CLDR", "True", "Deprecated", "True"}, 955 // {"region", "JT", "Description", "Johnston Island", "Preferred-Value", "UM", 956 // "CLDR", "True", "Deprecated", "True"}, 957 // {"region", "MI", "Description", "Midway Islands", "Preferred-Value", "UM", 958 // "CLDR", "True", "Deprecated", "True"}, 959 // {"region", "NQ", "Description", "Dronning Maud Land", "Preferred-Value", 960 // "AQ", "CLDR", "True", "Deprecated", "True"}, 961 // {"region", "PC", "Description", "Pacific Islands Trust Territory (divided 962 // into FM, MH, MP, and PW)", "Preferred-Value", "AQ", "CLDR", "True", 963 // "Deprecated", "True"}, 964 // {"region", "PU", "Description", "U.S. Miscellaneous Pacific Islands", 965 // "Preferred-Value", "UM", "CLDR", "True", "Deprecated", "True"}, 966 // {"region", "PZ", "Description", "Panama Canal Zone", "Preferred-Value", 967 // "PA", "CLDR", "True", "Deprecated", "True"}, 968 // {"region", "VD", "Description", "North Vietnam", "Preferred-Value", "VN", 969 // "CLDR", "True", "Deprecated", "True"}, 970 // {"region", "WK", "Description", "Wake Island", "Preferred-Value", "UM", 971 // "CLDR", "True", "Deprecated", "True"}, 972 }; 973 974 static final String registryName = CldrUtility.getProperty("registry", "language-subtag-registry"); 975 976 public enum LstrType { 977 language("und", "zxx", "mul", "mis", "root"), 978 script("Zzzz", "Zsym", "Zxxx", "Zmth"), 979 region("ZZ"), 980 variant(), 981 extlang(true, false), 982 legacy(true, false), 983 redundant(true, false), 984 /** specialized codes for validity; TODO: rename LstrType **/ 985 currency(false, true, "XXX"), 986 subdivision(false, true), 987 unit(false, true), 988 usage(false, true), 989 zone(false, true); 990 991 public final Set<String> specials; 992 public final String unknown; 993 public final boolean isLstr; 994 public final boolean isUnicode; 995 996 private LstrType(String... unknownValue) { 997 this(true, true, unknownValue); 998 } 999 1000 private LstrType(boolean lstr, boolean unicode, String... unknownValue) { 1001 unknown = unknownValue.length == 0 ? null : unknownValue[0]; 1002 LinkedHashSet<String> set = new LinkedHashSet<>(Arrays.asList(unknownValue)); 1003 if (unknown != null) { 1004 set.remove(unknown); 1005 } 1006 specials = Collections.unmodifiableSet(set); 1007 isLstr = lstr; 1008 isUnicode = unicode; 1009 } 1010 1011 // 1012 static final Pattern WELLFORMED = Pattern.compile("([0-9]{3}|[a-zA-Z]{2})[a-zA-Z0-9]{1,4}"); 1013 1014 boolean isWellFormed(String candidate) { 1015 switch (this) { 1016 case subdivision: 1017 return WELLFORMED.matcher(candidate).matches(); 1018 default: 1019 throw new UnsupportedOperationException(); 1020 } 1021 } 1022 1023 /** 1024 * Generate compatibility string, returning 'territory' instead of 'region', etc. 1025 */ 1026 public String toCompatString() { 1027 switch (this) { 1028 case region: return "territory"; 1029 case legacy: return "language"; 1030 case redundant: return "language"; 1031 default: return toString(); 1032 } 1033 } 1034 1035 /** 1036 * Create LstrType from string, allowing the compat string 'territory'. 1037 */ 1038 public static LstrType fromString(String rawType) { 1039 try { 1040 return valueOf(rawType); 1041 } catch (IllegalArgumentException e) { 1042 if ("territory".equals(rawType)) { 1043 return region; 1044 } 1045 throw e; 1046 } 1047 } 1048 } 1049 1050 public enum LstrField { 1051 Type, Subtag, Description, Added, Scope, Tag, Suppress_Script, Macrolanguage, Deprecated, Preferred_Value, Comments, Prefix, CLDR; 1052 public static LstrField from(String s) { 1053 return LstrField.valueOf(s.trim().replace("-", "_")); 1054 } 1055 } 1056 1057 static Map<String, Map<String, Map<String, String>>> LSTREG; 1058 static Map<LstrType, Map<String, Map<LstrField, String>>> LSTREG_ENUM; 1059 static Map<LstrType, Map<String, Map<LstrField, String>>> LSTREG_RAW; 1060 1061 /** 1062 * Returns a map like {extlang={aao={Added=2009-07-29, Description=Algerian Saharan Arabic, ...<br> 1063 * That is, type => subtype => map<tag,value>. Descriptions are concatenated together, separated by 1064 * DESCRIPTION_SEPARATOR. 1065 * 1066 * @return 1067 */ 1068 public static Map<String, Map<String, Map<String, String>>> getLStreg() { 1069 if (LSTREG == null) { 1070 initLstr(); 1071 } 1072 return LSTREG; 1073 } 1074 1075 /** 1076 * Returns a map like {extlang={aao={Added=2009-07-29, Description=Algerian Saharan Arabic, ...<br> 1077 * That is, type => subtype => map<tag,value>. Descriptions are concatenated together, separated by 1078 * DESCRIPTION_SEPARATOR. 1079 * 1080 * @return 1081 */ 1082 public static Map<LstrType, Map<String, Map<LstrField, String>>> getEnumLstreg() { 1083 if (LSTREG_ENUM == null) { 1084 initLstr(); 1085 } 1086 return LSTREG_ENUM; 1087 } 1088 1089 public static Map<LstrType, Map<String, Map<LstrField, String>>> getLstregEnumRaw() { 1090 if (LSTREG_ENUM == null) { 1091 initLstr(); 1092 } 1093 return LSTREG_RAW; 1094 } 1095 1096 private static void initLstr() { 1097 Map<LstrType, Map<String, Map<LstrField, String>>> result2 = new TreeMap<>(); 1098 1099 int lineNumber = 1; 1100 1101 Set<String> funnyTags = new TreeSet<>(); 1102 String line; 1103 try { 1104 BufferedReader lstreg = CldrUtility.getUTF8Data(registryName); 1105 LstrType lastType = null; 1106 String lastTag = null; 1107 Map<String, Map<LstrField, String>> subtagData = null; 1108 Map<LstrField, String> currentData = null; 1109 LstrField lastLabel = null; 1110 String lastRest = null; 1111 boolean inRealContent = false; 1112 // Map<String, String> translitCache = new HashMap<String, String>(); 1113 for (;; ++lineNumber) { 1114 line = lstreg.readLine(); 1115 if (line == null) 1116 break; 1117 if (line.length() == 0) 1118 continue; // skip blanks 1119 if (line.startsWith("File-Date: ")) { 1120 if (DEBUG) System.out.println("Language Subtag Registry: " + line); 1121 inRealContent = true; 1122 continue; 1123 } 1124 if (!inRealContent) { 1125 // skip until we get to real content 1126 continue; 1127 } 1128 // skip cruft 1129 if (line.startsWith("Internet-Draft")) { 1130 continue; 1131 } 1132 if (line.startsWith("Ewell")) { 1133 continue; 1134 } 1135 if (line.startsWith("\f")) { 1136 continue; 1137 } 1138 if (line.startsWith("4. Security Considerations")) { 1139 break; 1140 } 1141 1142 if (line.startsWith("%%")) 1143 continue; // skip separators (ok, since data starts with Type: 1144 if (line.startsWith(" ")) { 1145 currentData.put(lastLabel, lastRest + " " + line.trim()); 1146 continue; 1147 } 1148 1149 /* 1150 * Type: language Subtag: aa Description: Afar Added: 2005-10-16 1151 * Suppress-Script: Latn 1152 */ 1153 int pos2 = line.indexOf(':'); 1154 LstrField label = LstrField.from(line.substring(0, pos2)); 1155 String rest = line.substring(pos2 + 1).trim(); 1156 if (label == LstrField.Type) { 1157 lastType = rest.equals("grandfathered") ? 1158 LstrType.legacy : LstrType.fromString(rest); 1159 subtagData = CldrUtility.get(result2, lastType); 1160 if (subtagData == null) { 1161 result2.put(lastType, subtagData = new TreeMap<>()); 1162 } 1163 } else if (label == LstrField.Subtag 1164 || label == LstrField.Tag) { 1165 lastTag = rest; 1166 String endTag = null; 1167 // Subtag: qaa..qtz 1168 int pos = lastTag.indexOf(".."); 1169 if (pos >= 0) { 1170 endTag = lastTag.substring(pos + 2); 1171 lastTag = lastTag.substring(0, pos); 1172 } 1173 currentData = new TreeMap<>(); 1174 if (endTag == null) { 1175 putSubtagData(lastTag, subtagData, currentData); 1176 languageCount.add(lastType, 1); 1177 // System.out.println(languageCount.getCount(lastType) + "\t" + lastType + "\t" + lastTag); 1178 } else { 1179 for (; lastTag.compareTo(endTag) <= 0; lastTag = nextAlpha(lastTag)) { 1180 // System.out.println(">" + current); 1181 putSubtagData(lastTag, subtagData, currentData); 1182 languageCount.add(lastType, 1); 1183 // System.out.println(languageCount.getCount(lastType) + "\t" + lastType + "\t" + lastTag); 1184 } 1185 1186 } 1187 // label.equalsIgnoreCase("Added") || label.equalsIgnoreCase("Suppress-Script")) { 1188 // skip 1189 // } else if (pieces.length < 2) { 1190 // System.out.println("Odd Line: " + lastType + "\t" + lastTag + "\t" + line); 1191 } else { 1192 lastLabel = label; 1193 // The following code was removed because in the standard tests (TestAll) both lastRest and rest were always equal. 1194 // if(!translitCache.containsKey(rest)) { 1195 // lastRest = TransliteratorUtilities.fromXML.transliterate(rest); 1196 // translitCache.put(rest, lastRest); 1197 // if (!lastRest.equals(rest)) { 1198 // System.out.println(System.currentTimeMillis()+" initLStr: LastRest: '"+lastRest+"' Rest: '"+rest+"'"); 1199 // } 1200 // } else { 1201 // lastRest = translitCache.get(rest); 1202 // } 1203 lastRest = rest; 1204 String oldValue = CldrUtility.get(currentData, lastLabel); 1205 if (oldValue != null) { 1206 lastRest = oldValue + DESCRIPTION_SEPARATOR + lastRest; 1207 } 1208 currentData.put(lastLabel, lastRest); 1209 } 1210 } 1211 } catch (Exception e) { 1212 throw (RuntimeException) new IllegalArgumentException( 1213 "Can't process file: data/" 1214 + registryName + ";\t at line " + lineNumber).initCause(e); 1215 } finally { 1216 if (!funnyTags.isEmpty()) { 1217 if (DEBUG) 1218 System.out.println("Funny tags: " + funnyTags); 1219 } 1220 } 1221 // copy raw 1222 Map<LstrType, Map<String, Map<LstrField, String>>> rawLstreg = new TreeMap<>(); 1223 for (Entry<LstrType, Map<String, Map<LstrField, String>>> entry1 : result2.entrySet()) { 1224 LstrType key1 = entry1.getKey(); 1225 TreeMap<String, Map<LstrField, String>> raw1 = new TreeMap<>(); rawLstreg.put(key1, raw1)1226 rawLstreg.put(key1, raw1); 1227 for (Entry<String, Map<LstrField, String>> entry2 : entry1.getValue().entrySet()) { 1228 String key2 = entry2.getKey(); 1229 final Map<LstrField, String> value2 = entry2.getValue(); 1230 TreeMap<LstrField, String> raw2 = new TreeMap<>(); 1231 raw2.putAll(value2); raw1.put(key2, raw2)1232 raw1.put(key2, raw2); 1233 } 1234 } 1235 LSTREG_RAW = CldrUtility.protectCollection(rawLstreg); 1236 1237 // add extras 1238 for (int i = 0; i < extras.length; ++i) { 1239 Map<String, Map<LstrField, String>> subtagData = CldrUtility.get(result2, LstrType.fromString(extras[i][0])); 1240 if (subtagData == null) { LstrType.fromString(extras[i][0])1241 result2.put(LstrType.fromString(extras[i][0]), subtagData = new TreeMap<>()); 1242 } 1243 Map<LstrField, String> labelData = new TreeMap<>(); 1244 for (int j = 2; j < extras[i].length; j += 2) { LstrField.from(extras[i][j])1245 labelData.put(LstrField.from(extras[i][j]), extras[i][j + 1]); 1246 } 1247 Map<LstrField, String> old = CldrUtility.get(subtagData, extras[i][1]); 1248 if (old != null) { 1249 if (!"Private use".equals(CldrUtility.get(old, LstrField.Description))) { 1250 throw new IllegalArgumentException("REPLACING data for " + extras[i][1] + "\t" + old + "\twith" 1251 + labelData); 1252 } 1253 } 1254 if (false) { 1255 System.out.println((old != null ? "REPLACING" + "\t" + old : "ADDING") + 1256 " data for " + extras[i][1] + "\twith" + labelData); 1257 } subtagData.put(extras[i][1], labelData)1258 subtagData.put(extras[i][1], labelData); 1259 } 1260 // build compatibility map 1261 Map<String, Map<String, Map<String, String>>> result = new LinkedHashMap<>(); 1262 for (Entry<LstrType, Map<String, Map<LstrField, String>>> entry : result2.entrySet()) { 1263 Map<String, Map<String, String>> copy2 = new LinkedHashMap<>(); 1264 result.put(entry.getKey().toString(), copy2); 1265 for (Entry<String, Map<LstrField, String>> entry2 : entry.getValue().entrySet()) { 1266 Map<String, String> copy3 = new LinkedHashMap<>(); entry2.getKey()1267 copy2.put(entry2.getKey(), copy3); 1268 for (Entry<LstrField, String> entry3 : entry2.getValue().entrySet()) { entry3.getValue()1269 copy3.put(entry3.getKey().toString(), entry3.getValue()); 1270 } 1271 } 1272 } 1273 LSTREG = CldrUtility.protectCollection(result); 1274 LSTREG_ENUM = CldrUtility.protectCollection(result2); 1275 } 1276 1277 private static <K, K2, V> Map<K2, V> putSubtagData(K lastTag, Map<K, Map<K2, V>> subtagData, Map<K2, V> currentData) { 1278 Map<K2, V> oldData = subtagData.get(lastTag); 1279 if (oldData != null) { 1280 if (oldData.get("CLDR") != null) { 1281 System.out.println("overriding: " + lastTag + ", " + oldData); 1282 } else { 1283 throw new IllegalArgumentException("Duplicate tag: " + lastTag); 1284 } 1285 } 1286 return subtagData.put(lastTag, currentData); 1287 } 1288 1289 static Counter<LstrType> languageCount = new Counter<>(); 1290 1291 public static Counter<LstrType> getLanguageCount() { 1292 return languageCount; 1293 } 1294 1295 ZoneParser zoneParser = new ZoneParser(); 1296 1297 // static public final Set<String> MODERN_SCRIPTS = Collections 1298 // .unmodifiableSet(new TreeSet( 1299 // // "Bali " + 1300 // // "Bugi " + 1301 // // "Copt " + 1302 // // "Hano " + 1303 // // "Osma " + 1304 // // "Qaai " + 1305 // // "Sylo " + 1306 // // "Syrc " + 1307 // // "Tagb " + 1308 // // "Tglg " + 1309 // Arrays 1310 // .asList("Hans Hant Jpan Hrkt Kore Arab Armn Bali Beng Bopo Cans Cham Cher Cyrl Deva Ethi Geor Grek Gujr Guru Hani Hang Hebr Hira Knda Kana Kali Khmr Laoo Latn Lepc Limb Mlym Mong Mymr Talu Nkoo Olck Orya Saur Sinh Tale Taml Telu Thaa Thai Tibt Tfng Vaii Yiii" 1311 // .split("\\s+")))); 1312 1313 // updated to http://www.unicode.org/reports/tr31/tr31-9.html#Specific_Character_Adjustments 1314 1315 /** 1316 * @deprecated 1317 */ 1318 @Deprecated 1319 public Map<String, List<ZoneLine>> getZone_rules() { 1320 return zoneParser.getZone_rules(); 1321 } 1322 1323 /** 1324 * @deprecated 1325 */ 1326 @Deprecated 1327 public Map<String, List<String>> getZoneData() { 1328 return zoneParser.getZoneData(); 1329 } 1330 1331 /** 1332 * @deprecated 1333 */ 1334 @Deprecated 1335 public Set<String> getCanonicalTimeZones() { 1336 return zoneParser.getZoneData().keySet(); 1337 } 1338 1339 /** 1340 * @deprecated 1341 */ 1342 @Deprecated 1343 public Map<String, Set<String>> getCountryToZoneSet() { 1344 return zoneParser.getCountryToZoneSet(); 1345 } 1346 1347 /** 1348 * @deprecated 1349 */ 1350 @Deprecated 1351 public List<String> getDeprecatedZoneIDs() { 1352 return zoneParser.getDeprecatedZoneIDs(); 1353 } 1354 1355 /** 1356 * @deprecated 1357 */ 1358 @Deprecated 1359 public Comparator<String> getTZIDComparator() { 1360 return zoneParser.getTZIDComparator(); 1361 } 1362 1363 /** 1364 * @deprecated 1365 */ 1366 @Deprecated 1367 public Map<String, Set<String>> getZoneLinkNew_OldSet() { 1368 return zoneParser.getZoneLinkNew_OldSet(); 1369 } 1370 1371 /** 1372 * @deprecated 1373 */ 1374 @Deprecated 1375 public Map<String, String> getZoneLinkold_new() { 1376 return zoneParser.getZoneLinkold_new(); 1377 } 1378 1379 /** 1380 * @deprecated 1381 */ 1382 @Deprecated 1383 public Map getZoneRuleID_rules() { 1384 return zoneParser.getZoneRuleID_rules(); 1385 } 1386 1387 /** 1388 * @deprecated 1389 */ 1390 @Deprecated 1391 public Map<String, String> getZoneToCounty() { 1392 return zoneParser.getZoneToCounty(); 1393 } 1394 1395 /** 1396 * @deprecated 1397 */ 1398 @Deprecated 1399 public String getZoneVersion() { 1400 return zoneParser.getVersion(); 1401 } 1402 1403 public static String fixLanguageTag(String languageSubtag) { 1404 if (languageSubtag.equals("mo")) { // fix special cases 1405 return "ro"; 1406 } 1407 return languageSubtag; 1408 } 1409 1410 public boolean isModernLanguage(String languageCode) { 1411 if (getMoribundLanguages().contains(languageCode)) return false; 1412 Type type = Iso639Data.getType(languageCode); 1413 if (type == Type.Living) return true; 1414 if (languageCode.equals("eo")) return true; // exception for Esperanto 1415 // Scope scope = Iso639Data.getScope(languageCode); 1416 // if (scope == Scope.Collection) return false; 1417 return false; 1418 } 1419 1420 public static boolean isScriptModern(String script) { 1421 ScriptMetadata.Info info = ScriptMetadata.getInfo(script); 1422 if (info == null) { 1423 if (false) throw new IllegalArgumentException("No script metadata for: " + script); 1424 return false; 1425 } 1426 IdUsage idUsage = info.idUsage; 1427 return idUsage != IdUsage.EXCLUSION && idUsage != IdUsage.UNKNOWN; 1428 } 1429 1430 static final Pattern whitespace = PatternCache.get("\\s+"); 1431 static Set<String> filteredCurrencies = null; 1432 1433 public Set<String> getSurveyToolDisplayCodes(String type) { 1434 return getGoodAvailableCodes(type); 1435 } 1436 1437 static UnicodeSet COUNTRY = new UnicodeSet("[a-zA-Z]").freeze(); 1438 1439 /** 1440 * Quick check for whether valid country. Not complete: should use Validity 1441 * @param territory 1442 * @return 1443 */ 1444 public static boolean isCountry(String territory) { 1445 switch (territory) { 1446 case "ZZ": 1447 case "QO": 1448 case "EU": 1449 case "UN": 1450 case "EZ": 1451 return false; 1452 default: 1453 return territory.length() == 2 && COUNTRY.containsAll(territory); 1454 } 1455 } 1456 1457 public boolean isLstregPrivateUse(String type, String code) { 1458 Map<String, String> lStregData = getLStreg().get(type).get(code); 1459 return lStregData.get("Description").equalsIgnoreCase("private use"); 1460 } 1461 1462 public boolean isLstregDeprecated(String type, String code) { 1463 Map<String, String> lStregData = getLStreg().get(type).get(code); 1464 return lStregData.get("Deprecated") != null; 1465 } 1466 } 1467