• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = UResourceBundle.getBundleInstance(
214                 ICUData.ICU_BASE_NAME,
215                 "keyTypeData",
216                 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
217 
218         getKeyInfo(keyTypeDataRes.get("keyInfo"));
219         getTypeInfo(keyTypeDataRes.get("typeInfo"));
220 
221         UResourceBundle keyMapRes = keyTypeDataRes.get("keyMap");
222         UResourceBundle typeMapRes = keyTypeDataRes.get("typeMap");
223 
224         // alias data is optional
225         UResourceBundle typeAliasRes = null;
226         UResourceBundle bcpTypeAliasRes = null;
227 
228         try {
229             typeAliasRes = keyTypeDataRes.get("typeAlias");
230         } catch (MissingResourceException e) {
231             // fall through
232         }
233 
234         try {
235             bcpTypeAliasRes = keyTypeDataRes.get("bcpTypeAlias");
236         } catch (MissingResourceException e) {
237             // fall through
238         }
239 
240         // iterate through keyMap resource
241         UResourceBundleIterator keyMapItr = keyMapRes.getIterator();
242         Map<String,Set<String>> _Bcp47Keys = new LinkedHashMap<String,Set<String>>();
243 
244         while (keyMapItr.hasNext()) {
245             UResourceBundle keyMapEntry = keyMapItr.next();
246             String legacyKeyId = keyMapEntry.getKey();
247             String bcpKeyId = keyMapEntry.getString();
248 
249             boolean hasSameKey = false;
250             if (bcpKeyId.length() == 0) {
251                 // Empty value indicates that BCP key is same with the legacy key.
252                 bcpKeyId = legacyKeyId;
253                 hasSameKey = true;
254             }
255             final LinkedHashSet<String> _bcp47Types = new LinkedHashSet<String>();
256             _Bcp47Keys.put(bcpKeyId, Collections.unmodifiableSet(_bcp47Types));
257 
258             boolean isTZ = legacyKeyId.equals("timezone");
259 
260             // reverse type alias map
261             Map<String, Set<String>> typeAliasMap = null;
262             if (typeAliasRes != null) {
263                 UResourceBundle typeAliasResByKey = null;
264                 try {
265                     typeAliasResByKey = typeAliasRes.get(legacyKeyId);
266                 } catch (MissingResourceException e) {
267                     // fall through
268                 }
269                 if (typeAliasResByKey != null) {
270                     typeAliasMap = new HashMap<String, Set<String>>();
271                     UResourceBundleIterator typeAliasResItr = typeAliasResByKey.getIterator();
272                     while (typeAliasResItr.hasNext()) {
273                         UResourceBundle typeAliasDataEntry = typeAliasResItr.next();
274                         String from = typeAliasDataEntry.getKey();
275                         String to = typeAliasDataEntry.getString();
276                         if (isTZ) {
277                             from = from.replace(':', '/');
278                         }
279                         Set<String> aliasSet = typeAliasMap.get(to);
280                         if (aliasSet == null) {
281                             aliasSet = new HashSet<String>();
282                             typeAliasMap.put(to, aliasSet);
283                         }
284                         aliasSet.add(from);
285                     }
286                 }
287             }
288 
289             // reverse bcp type alias map
290             Map<String, Set<String>> bcpTypeAliasMap = null;
291             if (bcpTypeAliasRes != null) {
292                 UResourceBundle bcpTypeAliasResByKey = null;
293                 try {
294                     bcpTypeAliasResByKey = bcpTypeAliasRes.get(bcpKeyId);
295                 } catch (MissingResourceException e) {
296                     // fall through
297                 }
298                 if (bcpTypeAliasResByKey != null) {
299                     bcpTypeAliasMap = new HashMap<String, Set<String>>();
300                     UResourceBundleIterator bcpTypeAliasResItr = bcpTypeAliasResByKey.getIterator();
301                     while (bcpTypeAliasResItr.hasNext()) {
302                         UResourceBundle bcpTypeAliasDataEntry = bcpTypeAliasResItr.next();
303                         String from = bcpTypeAliasDataEntry.getKey();
304                         String to = bcpTypeAliasDataEntry.getString();
305                         Set<String> aliasSet = bcpTypeAliasMap.get(to);
306                         if (aliasSet == null) {
307                             aliasSet = new HashSet<String>();
308                             bcpTypeAliasMap.put(to, aliasSet);
309                         }
310                         aliasSet.add(from);
311                     }
312                 }
313             }
314 
315             Map<String, Type> typeDataMap = new HashMap<String, Type>();
316             EnumSet<SpecialType> specialTypeSet = null;
317 
318             // look up type map for the key, and walk through the mapping data
319             UResourceBundle typeMapResByKey = null;
320             try {
321                 typeMapResByKey = typeMapRes.get(legacyKeyId);
322             } catch (MissingResourceException e) {
323                 // type map for each key must exist
324                 assert false;
325             }
326             if (typeMapResByKey != null) {
327                 UResourceBundleIterator typeMapResByKeyItr = typeMapResByKey.getIterator();
328                 while (typeMapResByKeyItr.hasNext()) {
329                     UResourceBundle typeMapEntry = typeMapResByKeyItr.next();
330                     String legacyTypeId = typeMapEntry.getKey();
331                     String bcpTypeId = typeMapEntry.getString();
332 
333                     // special types
334                     final char first = legacyTypeId.charAt(0);
335                     final boolean isSpecialType = '9' < first && first < 'a' && bcpTypeId.length() == 0;
336                     if (isSpecialType) {
337                         if (specialTypeSet == null) {
338                             specialTypeSet = EnumSet.noneOf(SpecialType.class);
339                         }
340                         specialTypeSet.add(SpecialType.valueOf(legacyTypeId));
341                         _bcp47Types.add(legacyTypeId);
342                         continue;
343                     }
344 
345                     if (isTZ) {
346                         // a timezone key uses a colon instead of a slash in the resource.
347                         // e.g. America:Los_Angeles
348                         legacyTypeId = legacyTypeId.replace(':', '/');
349                     }
350 
351                     boolean hasSameType = false;
352                     if (bcpTypeId.length() == 0) {
353                         // Empty value indicates that BCP type is same with the legacy type.
354                         bcpTypeId = legacyTypeId;
355                         hasSameType = true;
356                     }
357                     _bcp47Types.add(bcpTypeId);
358 
359                     // Note: legacy type value should never be
360                     // equivalent to bcp type value of a different
361                     // type under the same key. So we use a single
362                     // map for lookup.
363                     Type t = new Type(legacyTypeId, bcpTypeId);
364                     typeDataMap.put(AsciiUtil.toLowerString(legacyTypeId), t);
365                     if (!hasSameType) {
366                         typeDataMap.put(AsciiUtil.toLowerString(bcpTypeId), t);
367                     }
368 
369                     // Also put aliases in the map
370                     if (typeAliasMap != null) {
371                         Set<String> typeAliasSet = typeAliasMap.get(legacyTypeId);
372                         if (typeAliasSet != null) {
373                             for (String alias : typeAliasSet) {
374                                 typeDataMap.put(AsciiUtil.toLowerString(alias), t);
375                             }
376                         }
377                     }
378                     if (bcpTypeAliasMap != null) {
379                         Set<String> bcpTypeAliasSet = bcpTypeAliasMap.get(bcpTypeId);
380                         if (bcpTypeAliasSet != null) {
381                             for (String alias : bcpTypeAliasSet) {
382                                 typeDataMap.put(AsciiUtil.toLowerString(alias), t);
383                             }
384                         }
385                     }
386                 }
387             }
388 
389             KeyData keyData = new KeyData(legacyKeyId, bcpKeyId, typeDataMap, specialTypeSet);
390 
391             KEYMAP.put(AsciiUtil.toLowerString(legacyKeyId), keyData);
392             if (!hasSameKey) {
393                 KEYMAP.put(AsciiUtil.toLowerString(bcpKeyId), keyData);
394             }
395         }
396         BCP47_KEYS = Collections.unmodifiableMap(_Bcp47Keys);
397     }
398 
399     static Set<String> DEPRECATED_KEYS = Collections.emptySet(); // default for no resources
400     static Map<String, ValueType> VALUE_TYPES = Collections.emptyMap(); // default for no resources
401     static Map<String, Set<String>> DEPRECATED_KEY_TYPES = Collections.emptyMap(); // default for no resources
402 
403     private enum KeyInfoType {deprecated, valueType}
404     private enum TypeInfoType {deprecated}
405 
406     /** Reads
407 keyInfo{
408     deprecated{
409         kh{"true"}
410         vt{"true"}
411     }
412     valueType{
413         ca{"incremental"}
414         kr{"multiple"}
415         vt{"multiple"}
416         x0{"any"}
417     }
418 }
419      */
420     private static void getKeyInfo(UResourceBundle keyInfoRes) {
421         Set<String> _deprecatedKeys = new LinkedHashSet<String>();
422         Map<String, ValueType> _valueTypes = new LinkedHashMap<String, ValueType>();
423         for (UResourceBundleIterator keyInfoIt = keyInfoRes.getIterator(); keyInfoIt.hasNext();) {
424             UResourceBundle keyInfoEntry = keyInfoIt.next();
425             String key = keyInfoEntry.getKey();
426             KeyInfoType keyInfo = KeyInfoType.valueOf(key);
427             for (UResourceBundleIterator keyInfoIt2 = keyInfoEntry.getIterator(); keyInfoIt2.hasNext();) {
428                 UResourceBundle keyInfoEntry2 = keyInfoIt2.next();
429                 String key2 = keyInfoEntry2.getKey();
430                 String value2 = keyInfoEntry2.getString();
431                 switch (keyInfo) {
432                 case deprecated:
433                     _deprecatedKeys.add(key2);
434                     break;
435                 case valueType:
436                     _valueTypes.put(key2, ValueType.valueOf(value2));
437                     break;
438                 }
439             }
440         }
441         DEPRECATED_KEYS = Collections.unmodifiableSet(_deprecatedKeys);
442         VALUE_TYPES = Collections.unmodifiableMap(_valueTypes);
443     }
444 
445     /** Reads:
446 typeInfo{
447     deprecated{
448         co{
449             direct{"true"}
450         }
451         tz{
452             camtr{"true"}
453         }
454     }
455 }
456      */
457     private static void getTypeInfo(UResourceBundle typeInfoRes) {
458         Map<String,Set<String>> _deprecatedKeyTypes = new LinkedHashMap<String,Set<String>>();
459         for (UResourceBundleIterator keyInfoIt = typeInfoRes.getIterator(); keyInfoIt.hasNext();) {
460             UResourceBundle keyInfoEntry = keyInfoIt.next();
461             String key = keyInfoEntry.getKey();
462             TypeInfoType typeInfo = TypeInfoType.valueOf(key);
463             for (UResourceBundleIterator keyInfoIt2 = keyInfoEntry.getIterator(); keyInfoIt2.hasNext();) {
464                 UResourceBundle keyInfoEntry2 = keyInfoIt2.next();
465                 String key2 = keyInfoEntry2.getKey();
466                 Set<String> _deprecatedTypes = new LinkedHashSet<String>();
467                 for (UResourceBundleIterator keyInfoIt3 = keyInfoEntry2.getIterator(); keyInfoIt3.hasNext();) {
468                     UResourceBundle keyInfoEntry3 = keyInfoIt3.next();
469                     String key3 = keyInfoEntry3.getKey();
470                     switch (typeInfo) { // allow for expansion
471                     case deprecated:
472                         _deprecatedTypes.add(key3);
473                         break;
474                     }
475                 }
476                 _deprecatedKeyTypes.put(key2, Collections.unmodifiableSet(_deprecatedTypes));
477             }
478         }
479         DEPRECATED_KEY_TYPES = Collections.unmodifiableMap(_deprecatedKeyTypes);
480     }
481 
482     //
483     // Note:    The key-type data is currently read from ICU resource bundle keyTypeData.res.
484     //          In future, we may import the data into code like below directly from CLDR to
485     //          avoid cyclic dependency between ULocale and UResourceBundle. For now, the code
486     //          below is just for proof of concept, and commented out.
487     //
488 
489     //    private static final String[][] TYPE_DATA_CA = {
490     //     // {<legacy type>, <bcp type - if different>},
491     //        {"buddhist", null},
492     //        {"chinese", null},
493     //        {"coptic", null},
494     //        {"dangi", null},
495     //        {"ethiopic", null},
496     //        {"ethiopic-amete-alem", "ethioaa"},
497     //        {"gregorian", "gregory"},
498     //        {"hebrew", null},
499     //        {"indian", null},
500     //        {"islamic", null},
501     //        {"islamic-civil", null},
502     //        {"islamic-rgsa", null},
503     //        {"islamic-tbla", null},
504     //        {"islamic-umalqura", null},
505     //        {"iso8601", null},
506     //        {"japanese", null},
507     //        {"persian", null},
508     //        {"roc", null},
509     //    };
510     //
511     //    private static final String[][] TYPE_DATA_KS = {
512     //     // {<legacy type>, <bcp type - if different>},
513     //        {"identical", "identic"},
514     //        {"primary", "level1"},
515     //        {"quaternary", "level4"},
516     //        {"secondary", "level2"},
517     //        {"tertiary", "level3"},
518     //    };
519     //
520     //    private static final String[][] TYPE_ALIAS_KS = {
521     //     // {<legacy alias>, <legacy canonical>},
522     //        {"quarternary", "quaternary"},
523     //    };
524     //
525     //    private static final String[][] BCP_TYPE_ALIAS_CA = {
526     //     // {<bcp deprecated>, <bcp preferred>
527     //        {"islamicc", "islamic-civil"},
528     //    };
529     //
530     //    private static final Object[][] KEY_DATA = {
531     //     // {<legacy key>, <bcp key - if different>, <type map>, <type alias>, <bcp type alias>},
532     //        {"calendar", "ca", TYPE_DATA_CA, null, BCP_TYPE_ALIAS_CA},
533     //        {"colstrength", "ks", TYPE_DATA_KS, TYPE_ALIAS_KS, null},
534     //    };
535 
536     private static final Object[][] KEY_DATA = {};
537 
538     @SuppressWarnings("unused")
539     private static void initFromTables() {
540         for (Object[] keyDataEntry : KEY_DATA) {
541             String legacyKeyId = (String)keyDataEntry[0];
542             String bcpKeyId = (String)keyDataEntry[1];
543             String[][] typeData = (String[][])keyDataEntry[2];
544             String[][] typeAliasData = (String[][])keyDataEntry[3];
545             String[][] bcpTypeAliasData = (String[][])keyDataEntry[4];
546 
547             boolean hasSameKey = false;
548             if (bcpKeyId == null) {
549                 bcpKeyId = legacyKeyId;
550                 hasSameKey = true;
551             }
552 
553             // reverse type alias map
554             Map<String, Set<String>> typeAliasMap = null;
555             if (typeAliasData != null) {
556                 typeAliasMap = new HashMap<String, Set<String>>();
557                 for (String[] typeAliasDataEntry : typeAliasData) {
558                     String from = typeAliasDataEntry[0];
559                     String to = typeAliasDataEntry[1];
560                     Set<String> aliasSet = typeAliasMap.get(to);
561                     if (aliasSet == null) {
562                         aliasSet = new HashSet<String>();
563                         typeAliasMap.put(to, aliasSet);
564                     }
565                     aliasSet.add(from);
566                 }
567             }
568 
569             // BCP type alias map data
570             Map<String, Set<String>> bcpTypeAliasMap = null;
571             if (bcpTypeAliasData != null) {
572                 bcpTypeAliasMap = new HashMap<String, Set<String>>();
573                 for (String[] bcpTypeAliasDataEntry : bcpTypeAliasData) {
574                     String from = bcpTypeAliasDataEntry[0];
575                     String to = bcpTypeAliasDataEntry[1];
576                     Set<String> aliasSet = bcpTypeAliasMap.get(to);
577                     if (aliasSet == null) {
578                         aliasSet = new HashSet<String>();
579                         bcpTypeAliasMap.put(to, aliasSet);
580                     }
581                     aliasSet.add(from);
582                 }
583             }
584 
585             // Type map data
586             assert typeData != null;
587             Map<String, Type> typeDataMap = new HashMap<String, Type>();
588             Set<SpecialType> specialTypeSet = null;
589 
590             for (String[] typeDataEntry : typeData) {
591                 String legacyTypeId = typeDataEntry[0];
592                 String bcpTypeId = typeDataEntry[1];
593 
594                 // special types
595                 boolean isSpecialType = false;
596                 for (SpecialType st : SpecialType.values()) {
597                     if (legacyTypeId.equals(st.toString())) {
598                         isSpecialType = true;
599                         if (specialTypeSet == null) {
600                             specialTypeSet = new HashSet<SpecialType>();
601                         }
602                         specialTypeSet.add(st);
603                         break;
604                     }
605                 }
606                 if (isSpecialType) {
607                     continue;
608                 }
609 
610                 boolean hasSameType = false;
611                 if (bcpTypeId == null) {
612                     bcpTypeId = legacyTypeId;
613                     hasSameType = true;
614                 }
615 
616                 // Note: legacy type value should never be
617                 // equivalent to bcp type value of a different
618                 // type under the same key. So we use a single
619                 // map for lookup.
620                 Type t = new Type(legacyTypeId, bcpTypeId);
621                 typeDataMap.put(AsciiUtil.toLowerString(legacyTypeId), t);
622                 if (!hasSameType) {
623                     typeDataMap.put(AsciiUtil.toLowerString(bcpTypeId), t);
624                 }
625 
626                 // Also put aliases in the index
627                 Set<String> typeAliasSet = typeAliasMap.get(legacyTypeId);
628                 if (typeAliasSet != null) {
629                     for (String alias : typeAliasSet) {
630                         typeDataMap.put(AsciiUtil.toLowerString(alias), t);
631                     }
632                 }
633                 Set<String> bcpTypeAliasSet = bcpTypeAliasMap.get(bcpTypeId);
634                 if (bcpTypeAliasSet != null) {
635                     for (String alias : bcpTypeAliasSet) {
636                         typeDataMap.put(AsciiUtil.toLowerString(alias), t);
637                     }
638                 }
639             }
640 
641             EnumSet<SpecialType> specialTypes = null;
642             if (specialTypeSet != null) {
643                 specialTypes = EnumSet.copyOf(specialTypeSet);
644             }
645 
646             KeyData keyData = new KeyData(legacyKeyId, bcpKeyId, typeDataMap, specialTypes);
647 
648             KEYMAP.put(AsciiUtil.toLowerString(legacyKeyId), keyData);
649             if (!hasSameKey) {
650                 KEYMAP.put(AsciiUtil.toLowerString(bcpKeyId), keyData);
651             }
652         }
653     }
654 
655     private static final Map<String, KeyData> KEYMAP = new HashMap<String, KeyData>();
656     private static Map<String, Set<String>> BCP47_KEYS;
657 
658     static {
659         // initFromTables();
660         initFromResourceBundle();
661     }
662 
663     public static Set<String> getBcp47Keys() {
664         return BCP47_KEYS.keySet();
665     };
666 
667     public static Set<String> getBcp47KeyTypes(String key) {
668         return BCP47_KEYS.get(key);
669     };
670 
671     public static boolean isDeprecated(String key) {
672         return DEPRECATED_KEYS.contains(key);
673     }
674 
675     public static boolean isDeprecated(String key, String type) {
676         Set<String> deprecatedTypes = DEPRECATED_KEY_TYPES.get(key);
677         if (deprecatedTypes == null) {
678             return false;
679         }
680         return deprecatedTypes.contains(type);
681     }
682 
683     public static ValueType getValueType(String key) {
684         ValueType type = VALUE_TYPES.get(key);
685         return type == null ? ValueType.single : type;
686     }
687 }
688