1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2014-2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.impl.locale; 10 11 import java.util.Collections; 12 import java.util.EnumSet; 13 import java.util.HashMap; 14 import java.util.HashSet; 15 import java.util.LinkedHashMap; 16 import java.util.LinkedHashSet; 17 import java.util.Map; 18 import java.util.MissingResourceException; 19 import java.util.Set; 20 import java.util.regex.Pattern; 21 22 import com.ibm.icu.impl.ICUData; 23 import com.ibm.icu.impl.ICUResourceBundle; 24 import com.ibm.icu.util.Output; 25 import com.ibm.icu.util.UResourceBundle; 26 import com.ibm.icu.util.UResourceBundleIterator; 27 28 /** 29 */ 30 public class KeyTypeData { 31 32 public enum ValueType { 33 single, multiple, incremental, any 34 } 35 36 private static abstract class SpecialTypeHandler { isWellFormed(String value)37 abstract boolean isWellFormed(String value); // doesn't test validity, just whether it is well formed. canonicalize(String value)38 String canonicalize(String value) { 39 return AsciiUtil.toLowerString(value); 40 } 41 } 42 43 private static class CodepointsTypeHandler extends SpecialTypeHandler { 44 private static final Pattern pat = Pattern.compile("[0-9a-fA-F]{4,6}(-[0-9a-fA-F]{4,6})*"); 45 @Override isWellFormed(String value)46 boolean isWellFormed(String value) { 47 return pat.matcher(value).matches(); 48 } 49 } 50 51 private static class ReorderCodeTypeHandler extends SpecialTypeHandler { 52 private static final Pattern pat = Pattern.compile("[a-zA-Z]{3,8}(-[a-zA-Z]{3,8})*"); 53 @Override isWellFormed(String value)54 boolean isWellFormed(String value) { 55 return pat.matcher(value).matches(); 56 } 57 } 58 59 private static class RgKeyValueTypeHandler extends SpecialTypeHandler { 60 private static final Pattern pat = Pattern.compile("([a-zA-Z]{2}|[0-9]{3})[zZ]{4}"); 61 @Override isWellFormed(String value)62 boolean isWellFormed(String value) { 63 return pat.matcher(value).matches(); 64 } 65 } 66 67 private static class SubdivisionKeyValueTypeHandler extends SpecialTypeHandler { 68 private static final Pattern pat = Pattern.compile("([a-zA-Z]{2}|[0-9]{3})"); 69 @Override isWellFormed(String value)70 boolean isWellFormed(String value) { 71 return pat.matcher(value).matches(); 72 } 73 } 74 75 private static class PrivateUseKeyValueTypeHandler extends SpecialTypeHandler { 76 private static final Pattern pat = Pattern.compile("[a-zA-Z0-9]{3,8}(-[a-zA-Z0-9]{3,8})*"); 77 @Override isWellFormed(String value)78 boolean isWellFormed(String value) { 79 return pat.matcher(value).matches(); 80 } 81 } 82 83 private enum SpecialType { 84 CODEPOINTS(new CodepointsTypeHandler()), 85 REORDER_CODE(new ReorderCodeTypeHandler()), 86 RG_KEY_VALUE(new RgKeyValueTypeHandler()), 87 SUBDIVISION_CODE(new SubdivisionKeyValueTypeHandler()), 88 PRIVATE_USE(new PrivateUseKeyValueTypeHandler()), 89 ; 90 SpecialTypeHandler handler; SpecialType(SpecialTypeHandler handler)91 SpecialType(SpecialTypeHandler handler) { 92 this.handler = handler; 93 } 94 }; 95 96 private static class KeyData { 97 String legacyId; 98 String bcpId; 99 Map<String, Type> typeMap; 100 EnumSet<SpecialType> specialTypes; 101 KeyData(String legacyId, String bcpId, Map<String, Type> typeMap, EnumSet<SpecialType> specialTypes)102 KeyData(String legacyId, String bcpId, Map<String, Type> typeMap, 103 EnumSet<SpecialType> specialTypes) { 104 this.legacyId = legacyId; 105 this.bcpId = bcpId; 106 this.typeMap = typeMap; 107 this.specialTypes = specialTypes; 108 } 109 } 110 111 private static class Type { 112 String legacyId; 113 String bcpId; 114 Type(String legacyId, String bcpId)115 Type(String legacyId, String bcpId) { 116 this.legacyId = legacyId; 117 this.bcpId = bcpId; 118 } 119 } 120 toBcpKey(String key)121 public static String toBcpKey(String key) { 122 key = AsciiUtil.toLowerString(key); 123 KeyData keyData = KEYMAP.get(key); 124 if (keyData != null) { 125 return keyData.bcpId; 126 } 127 return null; 128 } 129 toLegacyKey(String key)130 public static String toLegacyKey(String key) { 131 key = AsciiUtil.toLowerString(key); 132 KeyData keyData = KEYMAP.get(key); 133 if (keyData != null) { 134 return keyData.legacyId; 135 } 136 return null; 137 } 138 toBcpType(String key, String type, Output<Boolean> isKnownKey, Output<Boolean> isSpecialType)139 public static String toBcpType(String key, String type, 140 Output<Boolean> isKnownKey, Output<Boolean> isSpecialType) { 141 142 if (isKnownKey != null) { 143 isKnownKey.value = false; 144 } 145 if (isSpecialType != null) { 146 isSpecialType.value = false; 147 } 148 149 key = AsciiUtil.toLowerString(key); 150 type = AsciiUtil.toLowerString(type); 151 152 KeyData keyData = KEYMAP.get(key); 153 if (keyData != null) { 154 if (isKnownKey != null) { 155 isKnownKey.value = Boolean.TRUE; 156 } 157 Type t = keyData.typeMap.get(type); 158 if (t != null) { 159 return t.bcpId; 160 } 161 if (keyData.specialTypes != null) { 162 for (SpecialType st : keyData.specialTypes) { 163 if (st.handler.isWellFormed(type)) { 164 if (isSpecialType != null) { 165 isSpecialType.value = true; 166 } 167 return st.handler.canonicalize(type); 168 } 169 } 170 } 171 } 172 return null; 173 } 174 175 toLegacyType(String key, String type, Output<Boolean> isKnownKey, Output<Boolean> isSpecialType)176 public static String toLegacyType(String key, String type, 177 Output<Boolean> isKnownKey, Output<Boolean> isSpecialType) { 178 179 if (isKnownKey != null) { 180 isKnownKey.value = false; 181 } 182 if (isSpecialType != null) { 183 isSpecialType.value = false; 184 } 185 186 key = AsciiUtil.toLowerString(key); 187 type = AsciiUtil.toLowerString(type); 188 189 KeyData keyData = KEYMAP.get(key); 190 if (keyData != null) { 191 if (isKnownKey != null) { 192 isKnownKey.value = Boolean.TRUE; 193 } 194 Type t = keyData.typeMap.get(type); 195 if (t != null) { 196 return t.legacyId; 197 } 198 if (keyData.specialTypes != null) { 199 for (SpecialType st : keyData.specialTypes) { 200 if (st.handler.isWellFormed(type)) { 201 if (isSpecialType != null) { 202 isSpecialType.value = true; 203 } 204 return st.handler.canonicalize(type); 205 } 206 } 207 } 208 } 209 return null; 210 } 211 initFromResourceBundle()212 private static void initFromResourceBundle() { 213 UResourceBundle keyTypeDataRes = ICUResourceBundle.getBundleInstance( 214 ICUData.ICU_BASE_NAME, 215 "keyTypeData", 216 ICUResourceBundle.ICU_DATA_CLASS_LOADER, 217 ICUResourceBundle.OpenType.DIRECT); 218 219 getKeyInfo(keyTypeDataRes.get("keyInfo")); 220 getTypeInfo(keyTypeDataRes.get("typeInfo")); 221 222 UResourceBundle keyMapRes = keyTypeDataRes.get("keyMap"); 223 UResourceBundle typeMapRes = keyTypeDataRes.get("typeMap"); 224 225 // alias data is optional 226 UResourceBundle typeAliasRes = null; 227 UResourceBundle bcpTypeAliasRes = null; 228 229 try { 230 typeAliasRes = keyTypeDataRes.get("typeAlias"); 231 } catch (MissingResourceException e) { 232 // fall through 233 } 234 235 try { 236 bcpTypeAliasRes = keyTypeDataRes.get("bcpTypeAlias"); 237 } catch (MissingResourceException e) { 238 // fall through 239 } 240 241 // iterate through keyMap resource 242 UResourceBundleIterator keyMapItr = keyMapRes.getIterator(); 243 Map<String,Set<String>> _Bcp47Keys = new LinkedHashMap<String,Set<String>>(); 244 245 while (keyMapItr.hasNext()) { 246 UResourceBundle keyMapEntry = keyMapItr.next(); 247 String legacyKeyId = keyMapEntry.getKey(); 248 String bcpKeyId = keyMapEntry.getString(); 249 250 boolean hasSameKey = false; 251 if (bcpKeyId.length() == 0) { 252 // Empty value indicates that BCP key is same with the legacy key. 253 bcpKeyId = legacyKeyId; 254 hasSameKey = true; 255 } 256 final LinkedHashSet<String> _bcp47Types = new LinkedHashSet<String>(); 257 _Bcp47Keys.put(bcpKeyId, Collections.unmodifiableSet(_bcp47Types)); 258 259 boolean isTZ = legacyKeyId.equals("timezone"); 260 261 // reverse type alias map 262 Map<String, Set<String>> typeAliasMap = null; 263 if (typeAliasRes != null) { 264 UResourceBundle typeAliasResByKey = null; 265 try { 266 typeAliasResByKey = typeAliasRes.get(legacyKeyId); 267 } catch (MissingResourceException e) { 268 // fall through 269 } 270 if (typeAliasResByKey != null) { 271 typeAliasMap = new HashMap<String, Set<String>>(); 272 UResourceBundleIterator typeAliasResItr = typeAliasResByKey.getIterator(); 273 while (typeAliasResItr.hasNext()) { 274 UResourceBundle typeAliasDataEntry = typeAliasResItr.next(); 275 String from = typeAliasDataEntry.getKey(); 276 String to = typeAliasDataEntry.getString(); 277 if (isTZ) { 278 from = from.replace(':', '/'); 279 } 280 Set<String> aliasSet = typeAliasMap.get(to); 281 if (aliasSet == null) { 282 aliasSet = new HashSet<String>(); 283 typeAliasMap.put(to, aliasSet); 284 } 285 aliasSet.add(from); 286 } 287 } 288 } 289 290 // reverse bcp type alias map 291 Map<String, Set<String>> bcpTypeAliasMap = null; 292 if (bcpTypeAliasRes != null) { 293 UResourceBundle bcpTypeAliasResByKey = null; 294 try { 295 bcpTypeAliasResByKey = bcpTypeAliasRes.get(bcpKeyId); 296 } catch (MissingResourceException e) { 297 // fall through 298 } 299 if (bcpTypeAliasResByKey != null) { 300 bcpTypeAliasMap = new HashMap<String, Set<String>>(); 301 UResourceBundleIterator bcpTypeAliasResItr = bcpTypeAliasResByKey.getIterator(); 302 while (bcpTypeAliasResItr.hasNext()) { 303 UResourceBundle bcpTypeAliasDataEntry = bcpTypeAliasResItr.next(); 304 String from = bcpTypeAliasDataEntry.getKey(); 305 String to = bcpTypeAliasDataEntry.getString(); 306 Set<String> aliasSet = bcpTypeAliasMap.get(to); 307 if (aliasSet == null) { 308 aliasSet = new HashSet<String>(); 309 bcpTypeAliasMap.put(to, aliasSet); 310 } 311 aliasSet.add(from); 312 } 313 } 314 } 315 316 Map<String, Type> typeDataMap = new HashMap<String, Type>(); 317 EnumSet<SpecialType> specialTypeSet = null; 318 319 // look up type map for the key, and walk through the mapping data 320 UResourceBundle typeMapResByKey = null; 321 try { 322 typeMapResByKey = typeMapRes.get(legacyKeyId); 323 } catch (MissingResourceException e) { 324 // type map for each key must exist 325 assert false; 326 } 327 if (typeMapResByKey != null) { 328 UResourceBundleIterator typeMapResByKeyItr = typeMapResByKey.getIterator(); 329 while (typeMapResByKeyItr.hasNext()) { 330 UResourceBundle typeMapEntry = typeMapResByKeyItr.next(); 331 String legacyTypeId = typeMapEntry.getKey(); 332 String bcpTypeId = typeMapEntry.getString(); 333 334 // special types 335 final char first = legacyTypeId.charAt(0); 336 final boolean isSpecialType = '9' < first && first < 'a' && bcpTypeId.length() == 0; 337 if (isSpecialType) { 338 if (specialTypeSet == null) { 339 specialTypeSet = EnumSet.noneOf(SpecialType.class); 340 } 341 specialTypeSet.add(SpecialType.valueOf(legacyTypeId)); 342 _bcp47Types.add(legacyTypeId); 343 continue; 344 } 345 346 if (isTZ) { 347 // a timezone key uses a colon instead of a slash in the resource. 348 // e.g. America:Los_Angeles 349 legacyTypeId = legacyTypeId.replace(':', '/'); 350 } 351 352 boolean hasSameType = false; 353 if (bcpTypeId.length() == 0) { 354 // Empty value indicates that BCP type is same with the legacy type. 355 bcpTypeId = legacyTypeId; 356 hasSameType = true; 357 } 358 _bcp47Types.add(bcpTypeId); 359 360 // Note: legacy type value should never be 361 // equivalent to bcp type value of a different 362 // type under the same key. So we use a single 363 // map for lookup. 364 Type t = new Type(legacyTypeId, bcpTypeId); 365 typeDataMap.put(AsciiUtil.toLowerString(legacyTypeId), t); 366 if (!hasSameType) { 367 typeDataMap.put(AsciiUtil.toLowerString(bcpTypeId), t); 368 } 369 370 // Also put aliases in the map 371 if (typeAliasMap != null) { 372 Set<String> typeAliasSet = typeAliasMap.get(legacyTypeId); 373 if (typeAliasSet != null) { 374 for (String alias : typeAliasSet) { 375 typeDataMap.put(AsciiUtil.toLowerString(alias), t); 376 } 377 } 378 } 379 if (bcpTypeAliasMap != null) { 380 Set<String> bcpTypeAliasSet = bcpTypeAliasMap.get(bcpTypeId); 381 if (bcpTypeAliasSet != null) { 382 for (String alias : bcpTypeAliasSet) { 383 typeDataMap.put(AsciiUtil.toLowerString(alias), t); 384 } 385 } 386 } 387 } 388 } 389 390 KeyData keyData = new KeyData(legacyKeyId, bcpKeyId, typeDataMap, specialTypeSet); 391 392 KEYMAP.put(AsciiUtil.toLowerString(legacyKeyId), keyData); 393 if (!hasSameKey) { 394 KEYMAP.put(AsciiUtil.toLowerString(bcpKeyId), keyData); 395 } 396 } 397 BCP47_KEYS = Collections.unmodifiableMap(_Bcp47Keys); 398 } 399 400 static Set<String> DEPRECATED_KEYS = Collections.emptySet(); // default for no resources 401 static Map<String, ValueType> VALUE_TYPES = Collections.emptyMap(); // default for no resources 402 static Map<String, Set<String>> DEPRECATED_KEY_TYPES = Collections.emptyMap(); // default for no resources 403 404 private enum KeyInfoType {deprecated, valueType} 405 private enum TypeInfoType {deprecated} 406 407 /** Reads 408 keyInfo{ 409 deprecated{ 410 kh{"true"} 411 vt{"true"} 412 } 413 valueType{ 414 ca{"incremental"} 415 h0{"single"} 416 kr{"multiple"} 417 vt{"multiple"} 418 x0{"any"} 419 } 420 } 421 */ 422 private static void getKeyInfo(UResourceBundle keyInfoRes) { 423 Set<String> _deprecatedKeys = new LinkedHashSet<String>(); 424 Map<String, ValueType> _valueTypes = new LinkedHashMap<String, ValueType>(); 425 for (UResourceBundleIterator keyInfoIt = keyInfoRes.getIterator(); keyInfoIt.hasNext();) { 426 UResourceBundle keyInfoEntry = keyInfoIt.next(); 427 String key = keyInfoEntry.getKey(); 428 KeyInfoType keyInfo = KeyInfoType.valueOf(key); 429 for (UResourceBundleIterator keyInfoIt2 = keyInfoEntry.getIterator(); keyInfoIt2.hasNext();) { 430 UResourceBundle keyInfoEntry2 = keyInfoIt2.next(); 431 String key2 = keyInfoEntry2.getKey(); 432 String value2 = keyInfoEntry2.getString(); 433 switch (keyInfo) { 434 case deprecated: 435 _deprecatedKeys.add(key2); 436 break; 437 case valueType: 438 _valueTypes.put(key2, ValueType.valueOf(value2)); 439 break; 440 } 441 } 442 } 443 DEPRECATED_KEYS = Collections.unmodifiableSet(_deprecatedKeys); 444 VALUE_TYPES = Collections.unmodifiableMap(_valueTypes); 445 } 446 447 /** Reads: 448 typeInfo{ 449 deprecated{ 450 co{ 451 direct{"true"} 452 } 453 tz{ 454 camtr{"true"} 455 } 456 } 457 } 458 */ 459 private static void getTypeInfo(UResourceBundle typeInfoRes) { 460 Map<String,Set<String>> _deprecatedKeyTypes = new LinkedHashMap<String,Set<String>>(); 461 for (UResourceBundleIterator keyInfoIt = typeInfoRes.getIterator(); keyInfoIt.hasNext();) { 462 UResourceBundle keyInfoEntry = keyInfoIt.next(); 463 String key = keyInfoEntry.getKey(); 464 TypeInfoType typeInfo = TypeInfoType.valueOf(key); 465 for (UResourceBundleIterator keyInfoIt2 = keyInfoEntry.getIterator(); keyInfoIt2.hasNext();) { 466 UResourceBundle keyInfoEntry2 = keyInfoIt2.next(); 467 String key2 = keyInfoEntry2.getKey(); 468 Set<String> _deprecatedTypes = new LinkedHashSet<String>(); 469 for (UResourceBundleIterator keyInfoIt3 = keyInfoEntry2.getIterator(); keyInfoIt3.hasNext();) { 470 UResourceBundle keyInfoEntry3 = keyInfoIt3.next(); 471 String key3 = keyInfoEntry3.getKey(); 472 switch (typeInfo) { // allow for expansion 473 case deprecated: 474 _deprecatedTypes.add(key3); 475 break; 476 } 477 } 478 _deprecatedKeyTypes.put(key2, Collections.unmodifiableSet(_deprecatedTypes)); 479 } 480 } 481 DEPRECATED_KEY_TYPES = Collections.unmodifiableMap(_deprecatedKeyTypes); 482 } 483 484 // 485 // Note: The key-type data is currently read from ICU resource bundle keyTypeData.res. 486 // In future, we may import the data into code like below directly from CLDR to 487 // avoid cyclic dependency between ULocale and UResourceBundle. For now, the code 488 // below is just for proof of concept, and commented out. 489 // 490 491 // private static final String[][] TYPE_DATA_CA = { 492 // // {<legacy type>, <bcp type - if different>}, 493 // {"buddhist", null}, 494 // {"chinese", null}, 495 // {"coptic", null}, 496 // {"dangi", null}, 497 // {"ethiopic", null}, 498 // {"ethiopic-amete-alem", "ethioaa"}, 499 // {"gregorian", "gregory"}, 500 // {"hebrew", null}, 501 // {"indian", null}, 502 // {"islamic", null}, 503 // {"islamic-civil", null}, 504 // {"islamic-rgsa", null}, 505 // {"islamic-tbla", null}, 506 // {"islamic-umalqura", null}, 507 // {"iso8601", null}, 508 // {"japanese", null}, 509 // {"persian", null}, 510 // {"roc", null}, 511 // }; 512 // 513 // private static final String[][] TYPE_DATA_KS = { 514 // // {<legacy type>, <bcp type - if different>}, 515 // {"identical", "identic"}, 516 // {"primary", "level1"}, 517 // {"quaternary", "level4"}, 518 // {"secondary", "level2"}, 519 // {"tertiary", "level3"}, 520 // }; 521 // 522 // private static final String[][] TYPE_ALIAS_KS = { 523 // // {<legacy alias>, <legacy canonical>}, 524 // {"quarternary", "quaternary"}, 525 // }; 526 // 527 // private static final String[][] BCP_TYPE_ALIAS_CA = { 528 // // {<bcp deprecated>, <bcp preferred> 529 // {"islamicc", "islamic-civil"}, 530 // }; 531 // 532 // private static final Object[][] KEY_DATA = { 533 // // {<legacy key>, <bcp key - if different>, <type map>, <type alias>, <bcp type alias>}, 534 // {"calendar", "ca", TYPE_DATA_CA, null, BCP_TYPE_ALIAS_CA}, 535 // {"colstrength", "ks", TYPE_DATA_KS, TYPE_ALIAS_KS, null}, 536 // }; 537 538 private static final Object[][] KEY_DATA = {}; 539 540 @SuppressWarnings("unused") 541 private static void initFromTables() { 542 for (Object[] keyDataEntry : KEY_DATA) { 543 String legacyKeyId = (String)keyDataEntry[0]; 544 String bcpKeyId = (String)keyDataEntry[1]; 545 String[][] typeData = (String[][])keyDataEntry[2]; 546 String[][] typeAliasData = (String[][])keyDataEntry[3]; 547 String[][] bcpTypeAliasData = (String[][])keyDataEntry[4]; 548 549 boolean hasSameKey = false; 550 if (bcpKeyId == null) { 551 bcpKeyId = legacyKeyId; 552 hasSameKey = true; 553 } 554 555 // reverse type alias map 556 Map<String, Set<String>> typeAliasMap = null; 557 if (typeAliasData != null) { 558 typeAliasMap = new HashMap<String, Set<String>>(); 559 for (String[] typeAliasDataEntry : typeAliasData) { 560 String from = typeAliasDataEntry[0]; 561 String to = typeAliasDataEntry[1]; 562 Set<String> aliasSet = typeAliasMap.get(to); 563 if (aliasSet == null) { 564 aliasSet = new HashSet<String>(); 565 typeAliasMap.put(to, aliasSet); 566 } 567 aliasSet.add(from); 568 } 569 } 570 571 // BCP type alias map data 572 Map<String, Set<String>> bcpTypeAliasMap = null; 573 if (bcpTypeAliasData != null) { 574 bcpTypeAliasMap = new HashMap<String, Set<String>>(); 575 for (String[] bcpTypeAliasDataEntry : bcpTypeAliasData) { 576 String from = bcpTypeAliasDataEntry[0]; 577 String to = bcpTypeAliasDataEntry[1]; 578 Set<String> aliasSet = bcpTypeAliasMap.get(to); 579 if (aliasSet == null) { 580 aliasSet = new HashSet<String>(); 581 bcpTypeAliasMap.put(to, aliasSet); 582 } 583 aliasSet.add(from); 584 } 585 } 586 587 // Type map data 588 assert typeData != null; 589 Map<String, Type> typeDataMap = new HashMap<String, Type>(); 590 Set<SpecialType> specialTypeSet = null; 591 592 for (String[] typeDataEntry : typeData) { 593 String legacyTypeId = typeDataEntry[0]; 594 String bcpTypeId = typeDataEntry[1]; 595 596 // special types 597 boolean isSpecialType = false; 598 for (SpecialType st : SpecialType.values()) { 599 if (legacyTypeId.equals(st.toString())) { 600 isSpecialType = true; 601 if (specialTypeSet == null) { 602 specialTypeSet = new HashSet<SpecialType>(); 603 } 604 specialTypeSet.add(st); 605 break; 606 } 607 } 608 if (isSpecialType) { 609 continue; 610 } 611 612 boolean hasSameType = false; 613 if (bcpTypeId == null) { 614 bcpTypeId = legacyTypeId; 615 hasSameType = true; 616 } 617 618 // Note: legacy type value should never be 619 // equivalent to bcp type value of a different 620 // type under the same key. So we use a single 621 // map for lookup. 622 Type t = new Type(legacyTypeId, bcpTypeId); 623 typeDataMap.put(AsciiUtil.toLowerString(legacyTypeId), t); 624 if (!hasSameType) { 625 typeDataMap.put(AsciiUtil.toLowerString(bcpTypeId), t); 626 } 627 628 // Also put aliases in the index 629 Set<String> typeAliasSet = typeAliasMap.get(legacyTypeId); 630 if (typeAliasSet != null) { 631 for (String alias : typeAliasSet) { 632 typeDataMap.put(AsciiUtil.toLowerString(alias), t); 633 } 634 } 635 Set<String> bcpTypeAliasSet = bcpTypeAliasMap.get(bcpTypeId); 636 if (bcpTypeAliasSet != null) { 637 for (String alias : bcpTypeAliasSet) { 638 typeDataMap.put(AsciiUtil.toLowerString(alias), t); 639 } 640 } 641 } 642 643 EnumSet<SpecialType> specialTypes = null; 644 if (specialTypeSet != null) { 645 specialTypes = EnumSet.copyOf(specialTypeSet); 646 } 647 648 KeyData keyData = new KeyData(legacyKeyId, bcpKeyId, typeDataMap, specialTypes); 649 650 KEYMAP.put(AsciiUtil.toLowerString(legacyKeyId), keyData); 651 if (!hasSameKey) { 652 KEYMAP.put(AsciiUtil.toLowerString(bcpKeyId), keyData); 653 } 654 } 655 } 656 657 private static final Map<String, KeyData> KEYMAP = new HashMap<String, KeyData>(); 658 private static Map<String, Set<String>> BCP47_KEYS; 659 660 static { 661 // initFromTables(); 662 initFromResourceBundle(); 663 } 664 665 public static Set<String> getBcp47Keys() { 666 return BCP47_KEYS.keySet(); 667 }; 668 669 public static Set<String> getBcp47KeyTypes(String key) { 670 return BCP47_KEYS.get(key); 671 }; 672 673 public static boolean isDeprecated(String key) { 674 return DEPRECATED_KEYS.contains(key); 675 } 676 677 public static boolean isDeprecated(String key, String type) { 678 Set<String> deprecatedTypes = DEPRECATED_KEY_TYPES.get(key); 679 if (deprecatedTypes == null) { 680 return false; 681 } 682 return deprecatedTypes.contains(type); 683 } 684 685 public static ValueType getValueType(String key) { 686 ValueType type = VALUE_TYPES.get(key); 687 return type == null ? ValueType.single : type; 688 } 689 } 690