1/* GENERATED SOURCE. DO NOT MODIFY. */
2// © 2016 and later: Unicode, Inc. and others.
3// License & terms of use: http://www.unicode.org/copyright.html#License
4/*
5 *******************************************************************************
6 * Copyright (C) 2014-2016, International Business Machines Corporation and
7 * others. All Rights Reserved.
8 *******************************************************************************
9 */
10package android.icu.impl.locale;
11
12import java.util.Collections;
13import java.util.EnumSet;
14import java.util.HashMap;
15import java.util.HashSet;
16import java.util.LinkedHashMap;
17import java.util.LinkedHashSet;
18import java.util.Map;
19import java.util.MissingResourceException;
20import java.util.Set;
21import java.util.regex.Pattern;
22
23import android.icu.impl.ICUData;
24import android.icu.impl.ICUResourceBundle;
25import android.icu.util.Output;
26import android.icu.util.UResourceBundle;
27import android.icu.util.UResourceBundleIterator;
28
29/**
30 * @hide Only a subset of ICU is exposed in Android
31 */
32public class KeyTypeData {
33
34    public enum ValueType {
35        single, multiple, incremental, any
36    }
37
38    private static abstract class SpecialTypeHandler {
39        abstract boolean isWellFormed(String value); // doesn't test validity, just whether it is well formed.
40        String canonicalize(String value) {
41            return AsciiUtil.toLowerString(value);
42        }
43    }
44
45    private static class CodepointsTypeHandler extends SpecialTypeHandler {
46        private static final Pattern pat = Pattern.compile("[0-9a-fA-F]{4,6}(-[0-9a-fA-F]{4,6})*");
47        @Override
48        boolean isWellFormed(String value) {
49            return pat.matcher(value).matches();
50        }
51    }
52
53    private static class ReorderCodeTypeHandler extends SpecialTypeHandler {
54        private static final Pattern pat = Pattern.compile("[a-zA-Z]{3,8}(-[a-zA-Z]{3,8})*");
55        @Override
56        boolean isWellFormed(String value) {
57            return pat.matcher(value).matches();
58        }
59    }
60
61    private static class RgKeyValueTypeHandler extends SpecialTypeHandler {
62        private static final Pattern pat = Pattern.compile("([a-zA-Z]{2}|[0-9]{3})[zZ]{4}");
63        @Override
64        boolean isWellFormed(String value) {
65            return pat.matcher(value).matches();
66        }
67    }
68
69    private static class SubdivisionKeyValueTypeHandler extends SpecialTypeHandler {
70        private static final Pattern pat = Pattern.compile("([a-zA-Z]{2}|[0-9]{3})");
71        @Override
72        boolean isWellFormed(String value) {
73            return pat.matcher(value).matches();
74        }
75    }
76
77    private static class PrivateUseKeyValueTypeHandler extends SpecialTypeHandler {
78        private static final Pattern pat = Pattern.compile("[a-zA-Z0-9]{3,8}(-[a-zA-Z0-9]{3,8})*");
79        @Override
80        boolean isWellFormed(String value) {
81            return pat.matcher(value).matches();
82        }
83    }
84
85    private enum SpecialType {
86        CODEPOINTS(new CodepointsTypeHandler()),
87        REORDER_CODE(new ReorderCodeTypeHandler()),
88        RG_KEY_VALUE(new RgKeyValueTypeHandler()),
89        SUBDIVISION_CODE(new SubdivisionKeyValueTypeHandler()),
90        PRIVATE_USE(new PrivateUseKeyValueTypeHandler()),
91        ;
92        SpecialTypeHandler handler;
93        SpecialType(SpecialTypeHandler handler) {
94            this.handler = handler;
95        }
96    };
97
98    private static class KeyData {
99        String legacyId;
100        String bcpId;
101        Map<String, Type> typeMap;
102        EnumSet<SpecialType> specialTypes;
103
104        KeyData(String legacyId, String bcpId, Map<String, Type> typeMap,
105                EnumSet<SpecialType> specialTypes) {
106            this.legacyId = legacyId;
107            this.bcpId = bcpId;
108            this.typeMap = typeMap;
109            this.specialTypes = specialTypes;
110        }
111    }
112
113    private static class Type {
114        String legacyId;
115        String bcpId;
116
117        Type(String legacyId, String bcpId) {
118            this.legacyId = legacyId;
119            this.bcpId = bcpId;
120        }
121    }
122
123    public static String toBcpKey(String key) {
124        key = AsciiUtil.toLowerString(key);
125        KeyData keyData = KEYMAP.get(key);
126        if (keyData != null) {
127            return keyData.bcpId;
128        }
129        return null;
130    }
131
132    public static String toLegacyKey(String key) {
133        key = AsciiUtil.toLowerString(key);
134        KeyData keyData = KEYMAP.get(key);
135        if (keyData != null) {
136            return keyData.legacyId;
137        }
138        return null;
139    }
140
141    public static String toBcpType(String key, String type,
142            Output<Boolean> isKnownKey, Output<Boolean> isSpecialType) {
143
144        if (isKnownKey != null) {
145            isKnownKey.value = false;
146        }
147        if (isSpecialType != null) {
148            isSpecialType.value = false;
149        }
150
151        key = AsciiUtil.toLowerString(key);
152        type = AsciiUtil.toLowerString(type);
153
154        KeyData keyData = KEYMAP.get(key);
155        if (keyData != null) {
156            if (isKnownKey != null) {
157                isKnownKey.value = Boolean.TRUE;
158            }
159            Type t = keyData.typeMap.get(type);
160            if (t != null) {
161                return t.bcpId;
162            }
163            if (keyData.specialTypes != null) {
164                for (SpecialType st : keyData.specialTypes) {
165                    if (st.handler.isWellFormed(type)) {
166                        if (isSpecialType != null) {
167                            isSpecialType.value = true;
168                        }
169                        return st.handler.canonicalize(type);
170                    }
171                }
172            }
173        }
174        return null;
175    }
176
177
178    public static String toLegacyType(String key, String type,
179            Output<Boolean> isKnownKey, Output<Boolean> isSpecialType) {
180
181        if (isKnownKey != null) {
182            isKnownKey.value = false;
183        }
184        if (isSpecialType != null) {
185            isSpecialType.value = false;
186        }
187
188        key = AsciiUtil.toLowerString(key);
189        type = AsciiUtil.toLowerString(type);
190
191        KeyData keyData = KEYMAP.get(key);
192        if (keyData != null) {
193            if (isKnownKey != null) {
194                isKnownKey.value = Boolean.TRUE;
195            }
196            Type t = keyData.typeMap.get(type);
197            if (t != null) {
198                return t.legacyId;
199            }
200            if (keyData.specialTypes != null) {
201                for (SpecialType st : keyData.specialTypes) {
202                    if (st.handler.isWellFormed(type)) {
203                        if (isSpecialType != null) {
204                            isSpecialType.value = true;
205                        }
206                        return st.handler.canonicalize(type);
207                    }
208                }
209            }
210        }
211        return null;
212    }
213
214    private static void initFromResourceBundle() {
215        UResourceBundle keyTypeDataRes = UResourceBundle.getBundleInstance(
216                ICUData.ICU_BASE_NAME,
217                "keyTypeData",
218                ICUResourceBundle.ICU_DATA_CLASS_LOADER);
219
220        getKeyInfo(keyTypeDataRes.get("keyInfo"));
221        getTypeInfo(keyTypeDataRes.get("typeInfo"));
222
223        UResourceBundle keyMapRes = keyTypeDataRes.get("keyMap");
224        UResourceBundle typeMapRes = keyTypeDataRes.get("typeMap");
225
226        // alias data is optional
227        UResourceBundle typeAliasRes = null;
228        UResourceBundle bcpTypeAliasRes = null;
229
230        try {
231            typeAliasRes = keyTypeDataRes.get("typeAlias");
232        } catch (MissingResourceException e) {
233            // fall through
234        }
235
236        try {
237            bcpTypeAliasRes = keyTypeDataRes.get("bcpTypeAlias");
238        } catch (MissingResourceException e) {
239            // fall through
240        }
241
242        // iterate through keyMap resource
243        UResourceBundleIterator keyMapItr = keyMapRes.getIterator();
244        Map<String,Set<String>> _Bcp47Keys = new LinkedHashMap<String,Set<String>>();
245
246        while (keyMapItr.hasNext()) {
247            UResourceBundle keyMapEntry = keyMapItr.next();
248            String legacyKeyId = keyMapEntry.getKey();
249            String bcpKeyId = keyMapEntry.getString();
250
251            boolean hasSameKey = false;
252            if (bcpKeyId.length() == 0) {
253                // Empty value indicates that BCP key is same with the legacy key.
254                bcpKeyId = legacyKeyId;
255                hasSameKey = true;
256            }
257            final LinkedHashSet<String> _bcp47Types = new LinkedHashSet<String>();
258            _Bcp47Keys.put(bcpKeyId, Collections.unmodifiableSet(_bcp47Types));
259
260            boolean isTZ = legacyKeyId.equals("timezone");
261
262            // reverse type alias map
263            Map<String, Set<String>> typeAliasMap = null;
264            if (typeAliasRes != null) {
265                UResourceBundle typeAliasResByKey = null;
266                try {
267                    typeAliasResByKey = typeAliasRes.get(legacyKeyId);
268                } catch (MissingResourceException e) {
269                    // fall through
270                }
271                if (typeAliasResByKey != null) {
272                    typeAliasMap = new HashMap<String, Set<String>>();
273                    UResourceBundleIterator typeAliasResItr = typeAliasResByKey.getIterator();
274                    while (typeAliasResItr.hasNext()) {
275                        UResourceBundle typeAliasDataEntry = typeAliasResItr.next();
276                        String from = typeAliasDataEntry.getKey();
277                        String to = typeAliasDataEntry.getString();
278                        if (isTZ) {
279                            from = from.replace(':', '/');
280                        }
281                        Set<String> aliasSet = typeAliasMap.get(to);
282                        if (aliasSet == null) {
283                            aliasSet = new HashSet<String>();
284                            typeAliasMap.put(to, aliasSet);
285                        }
286                        aliasSet.add(from);
287                    }
288                }
289            }
290
291            // reverse bcp type alias map
292            Map<String, Set<String>> bcpTypeAliasMap = null;
293            if (bcpTypeAliasRes != null) {
294                UResourceBundle bcpTypeAliasResByKey = null;
295                try {
296                    bcpTypeAliasResByKey = bcpTypeAliasRes.get(bcpKeyId);
297                } catch (MissingResourceException e) {
298                    // fall through
299                }
300                if (bcpTypeAliasResByKey != null) {
301                    bcpTypeAliasMap = new HashMap<String, Set<String>>();
302                    UResourceBundleIterator bcpTypeAliasResItr = bcpTypeAliasResByKey.getIterator();
303                    while (bcpTypeAliasResItr.hasNext()) {
304                        UResourceBundle bcpTypeAliasDataEntry = bcpTypeAliasResItr.next();
305                        String from = bcpTypeAliasDataEntry.getKey();
306                        String to = bcpTypeAliasDataEntry.getString();
307                        Set<String> aliasSet = bcpTypeAliasMap.get(to);
308                        if (aliasSet == null) {
309                            aliasSet = new HashSet<String>();
310                            bcpTypeAliasMap.put(to, aliasSet);
311                        }
312                        aliasSet.add(from);
313                    }
314                }
315            }
316
317            Map<String, Type> typeDataMap = new HashMap<String, Type>();
318            EnumSet<SpecialType> specialTypeSet = null;
319
320            // look up type map for the key, and walk through the mapping data
321            UResourceBundle typeMapResByKey = null;
322            try {
323                typeMapResByKey = typeMapRes.get(legacyKeyId);
324            } catch (MissingResourceException e) {
325                // type map for each key must exist
326                assert false;
327            }
328            if (typeMapResByKey != null) {
329                UResourceBundleIterator typeMapResByKeyItr = typeMapResByKey.getIterator();
330                while (typeMapResByKeyItr.hasNext()) {
331                    UResourceBundle typeMapEntry = typeMapResByKeyItr.next();
332                    String legacyTypeId = typeMapEntry.getKey();
333                    String bcpTypeId = typeMapEntry.getString();
334
335                    // special types
336                    final char first = legacyTypeId.charAt(0);
337                    final boolean isSpecialType = '9' < first && first < 'a' && bcpTypeId.length() == 0;
338                    if (isSpecialType) {
339                        if (specialTypeSet == null) {
340                            specialTypeSet = EnumSet.noneOf(SpecialType.class);
341                        }
342                        specialTypeSet.add(SpecialType.valueOf(legacyTypeId));
343                        _bcp47Types.add(legacyTypeId);
344                        continue;
345                    }
346
347                    if (isTZ) {
348                        // a timezone key uses a colon instead of a slash in the resource.
349                        // e.g. America:Los_Angeles
350                        legacyTypeId = legacyTypeId.replace(':', '/');
351                    }
352
353                    boolean hasSameType = false;
354                    if (bcpTypeId.length() == 0) {
355                        // Empty value indicates that BCP type is same with the legacy type.
356                        bcpTypeId = legacyTypeId;
357                        hasSameType = true;
358                    }
359                    _bcp47Types.add(bcpTypeId);
360
361                    // Note: legacy type value should never be
362                    // equivalent to bcp type value of a different
363                    // type under the same key. So we use a single
364                    // map for lookup.
365                    Type t = new Type(legacyTypeId, bcpTypeId);
366                    typeDataMap.put(AsciiUtil.toLowerString(legacyTypeId), t);
367                    if (!hasSameType) {
368                        typeDataMap.put(AsciiUtil.toLowerString(bcpTypeId), t);
369                    }
370
371                    // Also put aliases in the map
372                    if (typeAliasMap != null) {
373                        Set<String> typeAliasSet = typeAliasMap.get(legacyTypeId);
374                        if (typeAliasSet != null) {
375                            for (String alias : typeAliasSet) {
376                                typeDataMap.put(AsciiUtil.toLowerString(alias), t);
377                            }
378                        }
379                    }
380                    if (bcpTypeAliasMap != null) {
381                        Set<String> bcpTypeAliasSet = bcpTypeAliasMap.get(bcpTypeId);
382                        if (bcpTypeAliasSet != null) {
383                            for (String alias : bcpTypeAliasSet) {
384                                typeDataMap.put(AsciiUtil.toLowerString(alias), t);
385                            }
386                        }
387                    }
388                }
389            }
390
391            KeyData keyData = new KeyData(legacyKeyId, bcpKeyId, typeDataMap, specialTypeSet);
392
393            KEYMAP.put(AsciiUtil.toLowerString(legacyKeyId), keyData);
394            if (!hasSameKey) {
395                KEYMAP.put(AsciiUtil.toLowerString(bcpKeyId), keyData);
396            }
397        }
398        BCP47_KEYS = Collections.unmodifiableMap(_Bcp47Keys);
399    }
400
401    static Set<String> DEPRECATED_KEYS = Collections.emptySet(); // default for no resources
402    static Map<String, ValueType> VALUE_TYPES = Collections.emptyMap(); // default for no resources
403    static Map<String, Set<String>> DEPRECATED_KEY_TYPES = Collections.emptyMap(); // default for no resources
404
405    private enum KeyInfoType {deprecated, valueType}
406    private enum TypeInfoType {deprecated}
407
408    /** Reads
409keyInfo{
410    deprecated{
411        kh{"true"}
412        vt{"true"}
413    }
414    valueType{
415        ca{"incremental"}
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:
448typeInfo{
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