InternalLocaleBuilder.java revision 1537b2f39245c07b00aa78c3600f7aebcb172490
12ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/* GENERATED SOURCE. DO NOT MODIFY. */
22ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/*
32ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *******************************************************************************
42ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Copyright (C) 2009-2010, International Business Machines Corporation and    *
52ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * others. All Rights Reserved.                                                *
62ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *******************************************************************************
72ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
82ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpackage android.icu.impl.locale;
92ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.ArrayList;
112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.HashMap;
122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.HashSet;
132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.List;
142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Set;
152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
161537b2f39245c07b00aa78c3600f7aebcb172490Neil Fuller/**
171537b2f39245c07b00aa78c3600f7aebcb172490Neil Fuller * @hide Only a subset of ICU is exposed in Android
181537b2f39245c07b00aa78c3600f7aebcb172490Neil Fuller * @hide All android.icu classes are currently hidden
19836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller */
202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpublic final class InternalLocaleBuilder {
212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final boolean JDKIMPL = false;
232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private String _language = "";
252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private String _script = "";
262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private String _region = "";
272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private String _variant = "";
282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final CaseInsensitiveChar PRIVUSE_KEY = new CaseInsensitiveChar(LanguageTag.PRIVATEUSE.charAt(0));
302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private HashMap<CaseInsensitiveChar, String> _extensions;
322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private HashSet<CaseInsensitiveString> _uattributes;
332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private HashMap<CaseInsensitiveString, String> _ukeywords;
342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder() {
372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder setLanguage(String language) throws LocaleSyntaxException {
402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (language == null || language.length() == 0) {
412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _language = "";
422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!LanguageTag.isLanguage(language)) {
442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new LocaleSyntaxException("Ill-formed language: " + language, 0);
452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _language = language;
472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder setScript(String script) throws LocaleSyntaxException {
522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (script == null || script.length() == 0) {
532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _script = "";
542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!LanguageTag.isScript(script)) {
562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new LocaleSyntaxException("Ill-formed script: " + script, 0);
572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _script = script;
592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder setRegion(String region) throws LocaleSyntaxException {
642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (region == null || region.length() == 0) {
652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _region = "";
662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!LanguageTag.isRegion(region)) {
682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new LocaleSyntaxException("Ill-formed region: " + region, 0);
692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _region = region;
712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder setVariant(String variant) throws LocaleSyntaxException {
762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (variant == null || variant.length() == 0) {
772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _variant = "";
782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // normalize separators to "_"
802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String var = variant.replaceAll(LanguageTag.SEP, BaseLocale.SEP);
812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int errIdx = checkVariants(var, BaseLocale.SEP);
822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (errIdx != -1) {
832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx);
842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _variant = var;
862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder addUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException {
912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (attribute == null || !UnicodeLocaleExtension.isAttribute(attribute)) {
922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute);
932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Use case insensitive string to prevent duplication
952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_uattributes == null) {
962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _uattributes = new HashSet<CaseInsensitiveString>(4);
972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _uattributes.add(new CaseInsensitiveString(attribute));
992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
1002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder removeUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException {
1032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (attribute == null || !UnicodeLocaleExtension.isAttribute(attribute)) {
1042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute);
1052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_uattributes != null) {
1072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _uattributes.remove(new CaseInsensitiveString(attribute));
1082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
1102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder setUnicodeLocaleKeyword(String key, String type) throws LocaleSyntaxException {
1132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!UnicodeLocaleExtension.isKey(key)) {
1142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new LocaleSyntaxException("Ill-formed Unicode locale keyword key: " + key);
1152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        CaseInsensitiveString cikey = new CaseInsensitiveString(key);
1182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (type == null) {
1192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (_ukeywords != null) {
1202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // null type is used for remove the key
1212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                _ukeywords.remove(cikey);
1222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
1232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
1242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (type.length() != 0) {
1252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // normalize separator to "-"
1262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String tp = type.replaceAll(BaseLocale.SEP, LanguageTag.SEP);
1272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // validate
1282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                StringTokenIterator itr = new StringTokenIterator(tp, LanguageTag.SEP);
1292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                while (!itr.isDone()) {
1302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    String s = itr.current();
1312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (!UnicodeLocaleExtension.isTypeSubtag(s)) {
1322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        throw new LocaleSyntaxException("Ill-formed Unicode locale keyword type: " + type, itr.currentStart());
1332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
1342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    itr.next();
1352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
1362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
1372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (_ukeywords == null) {
1382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                _ukeywords = new HashMap<CaseInsensitiveString, String>(4);
1392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
1402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _ukeywords.put(cikey, type);
1412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
1432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder setExtension(char singleton, String value) throws LocaleSyntaxException {
1462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // validate key
1472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean isBcpPrivateuse = LanguageTag.isPrivateusePrefixChar(singleton);
1482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!isBcpPrivateuse && !LanguageTag.isExtensionSingletonChar(singleton)) {
1492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new LocaleSyntaxException("Ill-formed extension key: " + singleton);
1502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean remove = (value == null || value.length() == 0);
1532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        CaseInsensitiveChar key = new CaseInsensitiveChar(singleton);
1542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (remove) {
1562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (UnicodeLocaleExtension.isSingletonChar(key.value())) {
1572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // clear entire Unicode locale extension
1582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (_uattributes != null) {
1592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _uattributes.clear();
1602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
1612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (_ukeywords != null) {
1622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _ukeywords.clear();
1632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
1642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
1652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (_extensions != null && _extensions.containsKey(key)) {
1662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _extensions.remove(key);
1672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
1682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
1692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
1702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // validate value
1712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String val = value.replaceAll(BaseLocale.SEP, LanguageTag.SEP);
1722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            StringTokenIterator itr = new StringTokenIterator(val, LanguageTag.SEP);
1732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            while (!itr.isDone()) {
1742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String s = itr.current();
1752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                boolean validSubtag;
1762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (isBcpPrivateuse) {
1772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    validSubtag = LanguageTag.isPrivateuseSubtag(s);
1782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
1792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    validSubtag = LanguageTag.isExtensionSubtag(s);
1802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
1812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (!validSubtag) {
1822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    throw new LocaleSyntaxException("Ill-formed extension value: " + s, itr.currentStart());
1832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
1842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                itr.next();
1852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
1862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (UnicodeLocaleExtension.isSingletonChar(key.value())) {
1882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                setUnicodeLocaleExtension(val);
1892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
1902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (_extensions == null) {
1912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _extensions = new HashMap<CaseInsensitiveChar, String>(4);
1922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
1932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                _extensions.put(key, val);
1942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
1952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
1972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
2002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Set extension/private subtags in a single string representation
2012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder setExtensions(String subtags) throws LocaleSyntaxException {
2032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (subtags == null || subtags.length() == 0) {
2042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            clearExtensions();
2052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return this;
2062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        subtags = subtags.replaceAll(BaseLocale.SEP, LanguageTag.SEP);
2082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP);
2092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        List<String> extensions = null;
2112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String privateuse = null;
2122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int parsed = 0;
2142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int start;
2152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Make a list of extension subtags
2172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        while (!itr.isDone()) {
2182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String s = itr.current();
2192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (LanguageTag.isExtensionSingleton(s)) {
2202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                start = itr.currentStart();
2212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String singleton = s;
2222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                StringBuilder sb = new StringBuilder(singleton);
2232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                itr.next();
2252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                while (!itr.isDone()) {
2262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    s = itr.current();
2272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (LanguageTag.isExtensionSubtag(s)) {
2282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        sb.append(LanguageTag.SEP).append(s);
2292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        parsed = itr.currentEnd();
2302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    } else {
2312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        break;
2322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
2332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    itr.next();
2342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (parsed < start) {
2372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    throw new LocaleSyntaxException("Incomplete extension '" + singleton + "'", start);
2382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (extensions == null) {
2412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    extensions = new ArrayList<String>(4);
2422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                extensions.add(sb.toString());
2442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
2452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                break;
2462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!itr.isDone()) {
2492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String s = itr.current();
2502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (LanguageTag.isPrivateusePrefix(s)) {
2512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                start = itr.currentStart();
2522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                StringBuilder sb = new StringBuilder(s);
2532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                itr.next();
2552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                while (!itr.isDone()) {
2562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    s = itr.current();
2572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (!LanguageTag.isPrivateuseSubtag(s)) {
2582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        break;
2592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
2602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    sb.append(LanguageTag.SEP).append(s);
2612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    parsed = itr.currentEnd();
2622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    itr.next();
2642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (parsed <= start) {
2662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    throw new LocaleSyntaxException("Incomplete privateuse:" + subtags.substring(start), start);
2672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
2682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    privateuse = sb.toString();
2692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!itr.isDone()) {
2742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new LocaleSyntaxException("Ill-formed extension subtags:" + subtags.substring(itr.currentStart()), itr.currentStart());
2752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return setExtensions(extensions, privateuse);
2782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
2812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Set a list of BCP47 extensions and private use subtags
2822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * BCP47 extensions are already validated and well-formed, but may contain duplicates
2832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private InternalLocaleBuilder setExtensions(List<String> bcpExtensions, String privateuse) {
2852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        clearExtensions();
2862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (bcpExtensions != null && bcpExtensions.size() > 0) {
2882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            HashSet<CaseInsensitiveChar> processedExtensions = new HashSet<CaseInsensitiveChar>(bcpExtensions.size());
2892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (String bcpExt : bcpExtensions) {
2902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                CaseInsensitiveChar key = new CaseInsensitiveChar(bcpExt.charAt(0));
2912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // ignore duplicates
2922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (!processedExtensions.contains(key)) {
2932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // each extension string contains singleton, e.g. "a-abc-def"
2942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (UnicodeLocaleExtension.isSingletonChar(key.value())) {
2952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        setUnicodeLocaleExtension(bcpExt.substring(2));
2962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    } else {
2972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (_extensions == null) {
2982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            _extensions = new HashMap<CaseInsensitiveChar, String>(4);
2992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
3002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        _extensions.put(key, bcpExt.substring(2));
3012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
3022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
3032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (privateuse != null && privateuse.length() > 0) {
3062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // privateuse string contains prefix, e.g. "x-abc-def"
3072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (_extensions == null) {
3082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                _extensions = new HashMap<CaseInsensitiveChar, String>(1);
3092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _extensions.put(new CaseInsensitiveChar(privateuse.charAt(0)), privateuse.substring(2));
3112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
3142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
3172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Reset Builder's internal state with the given language tag
3182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder setLanguageTag(LanguageTag langtag) {
3202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        clear();
3212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (langtag.getExtlangs().size() > 0) {
3222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _language = langtag.getExtlangs().get(0);
3232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
3242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String language = langtag.getLanguage();
3252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!language.equals(LanguageTag.UNDETERMINED)) {
3262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                _language = language;
3272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _script = langtag.getScript();
3302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _region = langtag.getRegion();
3312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        List<String> bcpVariants = langtag.getVariants();
3332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (bcpVariants.size() > 0) {
3342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            StringBuilder var = new StringBuilder(bcpVariants.get(0));
3352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (int i = 1; i < bcpVariants.size(); i++) {
3362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                var.append(BaseLocale.SEP).append(bcpVariants.get(i));
3372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _variant = var.toString();
3392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        setExtensions(langtag.getExtensions(), langtag.getPrivateuse());
3422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
3442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder setLocale(BaseLocale base, LocaleExtensions extensions) throws LocaleSyntaxException {
3472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String language = base.getLanguage();
3482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String script = base.getScript();
3492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String region = base.getRegion();
3502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String variant = base.getVariant();
3512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (JDKIMPL) {
3532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Special backward compatibility support
3542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Exception 1 - ja_JP_JP
3562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (language.equals("ja") && region.equals("JP") && variant.equals("JP")) {
3572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // When locale ja_JP_JP is created, ca-japanese is always there.
3582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // The builder ignores the variant "JP"
3592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                assert("japanese".equals(extensions.getUnicodeLocaleType("ca")));
3602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                variant = "";
3612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Exception 2 - th_TH_TH
3632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            else if (language.equals("th") && region.equals("TH") && variant.equals("TH")) {
3642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // When locale th_TH_TH is created, nu-thai is always there.
3652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // The builder ignores the variant "TH"
3662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                assert("thai".equals(extensions.getUnicodeLocaleType("nu")));
3672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                variant = "";
3682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Exception 3 - no_NO_NY
3702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            else if (language.equals("no") && region.equals("NO") && variant.equals("NY")) {
3712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // no_NO_NY is a valid locale and used by Java 6 or older versions.
3722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // The build ignores the variant "NY" and change the language to "nn".
3732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                language = "nn";
3742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                variant = "";
3752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Validate base locale fields before updating internal state.
3792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // LocaleExtensions always store validated/canonicalized values,
3802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // so no checks are necessary.
3812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (language.length() > 0 && !LanguageTag.isLanguage(language)) {
3822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new LocaleSyntaxException("Ill-formed language: " + language);
3832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (script.length() > 0 && !LanguageTag.isScript(script)) {
3862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new LocaleSyntaxException("Ill-formed script: " + script);
3872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (region.length() > 0 && !LanguageTag.isRegion(region)) {
3902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new LocaleSyntaxException("Ill-formed region: " + region);
3912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (variant.length() > 0) {
3942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int errIdx = checkVariants(variant, BaseLocale.SEP);
3952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (errIdx != -1) {
3962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx);
3972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // The input locale is validated at this point.
4012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Now, updating builder's internal fields.
4022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _language = language;
4032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _script = script;
4042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _region = region;
4052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _variant = variant;
4062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        clearExtensions();
4072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Set<Character> extKeys = (extensions == null) ? null : extensions.getKeys();
4092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (extKeys != null) {
4102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // map extensions back to builder's internal format
4112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (Character key : extKeys) {
4122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                Extension e = extensions.getExtension(key);
4132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (e instanceof UnicodeLocaleExtension) {
4142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    UnicodeLocaleExtension ue = (UnicodeLocaleExtension)e;
4152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    for (String uatr : ue.getUnicodeLocaleAttributes()) {
4162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (_uattributes == null) {
4172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            _uattributes = new HashSet<CaseInsensitiveString>(4);
4182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
4192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        _uattributes.add(new CaseInsensitiveString(uatr));
4202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    for (String ukey : ue.getUnicodeLocaleKeys()) {
4222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (_ukeywords == null) {
4232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            _ukeywords = new HashMap<CaseInsensitiveString, String>(4);
4242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
4252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        _ukeywords.put(new CaseInsensitiveString(ukey), ue.getUnicodeLocaleType(ukey));
4262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
4282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (_extensions == null) {
4292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        _extensions = new HashMap<CaseInsensitiveChar, String>(4);
4302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _extensions.put(new CaseInsensitiveChar(key.charValue()), e.getValue());
4322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
4332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
4342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
4362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder clear() {
4392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _language = "";
4402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _script = "";
4412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _region = "";
4422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _variant = "";
4432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        clearExtensions();
4442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
4452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public InternalLocaleBuilder clearExtensions() {
4482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_extensions != null) {
4492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _extensions.clear();
4502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_uattributes != null) {
4522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _uattributes.clear();
4532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_ukeywords != null) {
4552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _ukeywords.clear();
4562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
4582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public BaseLocale getBaseLocale() {
4612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String language = _language;
4622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String script = _script;
4632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String region = _region;
4642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String variant = _variant;
4652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Special private use subtag sequence identified by "lvariant" will be
4672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // interpreted as Java variant.
4682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_extensions != null) {
4692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String privuse = _extensions.get(PRIVUSE_KEY);
4702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (privuse != null) {
4712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                StringTokenIterator itr = new StringTokenIterator(privuse, LanguageTag.SEP);
4722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                boolean sawPrefix = false;
4732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                int privVarStart = -1;
4742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                while (!itr.isDone()) {
4752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (sawPrefix) {
4762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        privVarStart = itr.currentStart();
4772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        break;
4782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (AsciiUtil.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) {
4802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        sawPrefix = true;
4812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    itr.next();
4832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
4842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (privVarStart != -1) {
4852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    StringBuilder sb = new StringBuilder(variant);
4862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (sb.length() != 0) {
4872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        sb.append(BaseLocale.SEP);
4882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    sb.append(privuse.substring(privVarStart).replaceAll(LanguageTag.SEP, BaseLocale.SEP));
4902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    variant = sb.toString();
4912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
4922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
4932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return BaseLocale.getInstance(language, script, region, variant);
4962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public LocaleExtensions getLocaleExtensions() {
4992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ((_extensions == null || _extensions.size() == 0)
5002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                && (_uattributes == null || _uattributes.size() == 0)
5012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                && (_ukeywords == null || _ukeywords.size() == 0)) {
5022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return LocaleExtensions.EMPTY_EXTENSIONS;
5032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new LocaleExtensions(_extensions, _uattributes, _ukeywords);
5062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
5092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Remove special private use subtag sequence identified by "lvariant"
5102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and return the rest. Only used by LocaleExtensions
5112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static String removePrivateuseVariant(String privuseVal) {
5132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        StringTokenIterator itr = new StringTokenIterator(privuseVal, LanguageTag.SEP);
5142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Note: privateuse value "abc-lvariant" is unchanged
5162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // because no subtags after "lvariant".
5172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int prefixStart = -1;
5192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean sawPrivuseVar = false;
5202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        while (!itr.isDone()) {
5212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (prefixStart != -1) {
5222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Note: privateuse value "abc-lvariant" is unchanged
5232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // because no subtags after "lvariant".
5242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                sawPrivuseVar = true;
5252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                break;
5262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (AsciiUtil.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) {
5282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                prefixStart = itr.currentStart();
5292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            itr.next();
5312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!sawPrivuseVar) {
5332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return privuseVal;
5342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        assert(prefixStart == 0 || prefixStart > 1);
5372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return (prefixStart == 0) ? null : privuseVal.substring(0, prefixStart -1);
5382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
5412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Check if the given variant subtags separated by the given
5422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * separator(s) are valid
5432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private int checkVariants(String variants, String sep) {
5452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        StringTokenIterator itr = new StringTokenIterator(variants, sep);
5462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        while (!itr.isDone()) {
5472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String s = itr.current();
5482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!LanguageTag.isVariant(s)) {
5492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return itr.currentStart();
5502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            itr.next();
5522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return -1;
5542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
5572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Private methods parsing Unicode Locale Extension subtags.
5582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Duplicated attributes/keywords will be ignored.
5592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * The input must be a valid extension subtags (excluding singleton).
5602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private void setUnicodeLocaleExtension(String subtags) {
5622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // wipe out existing attributes/keywords
5632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_uattributes != null) {
5642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _uattributes.clear();
5652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_ukeywords != null) {
5672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _ukeywords.clear();
5682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP);
5712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // parse attributes
5732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        while (!itr.isDone()) {
5742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!UnicodeLocaleExtension.isAttribute(itr.current())) {
5752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                break;
5762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (_uattributes == null) {
5782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                _uattributes = new HashSet<CaseInsensitiveString>(4);
5792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _uattributes.add(new CaseInsensitiveString(itr.current()));
5812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            itr.next();
5822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // parse keywords
5852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        CaseInsensitiveString key = null;
5862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String type;
5872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int typeStart = -1;
5882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int typeEnd = -1;
5892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        while (!itr.isDone()) {
5902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (key != null) {
5912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (UnicodeLocaleExtension.isKey(itr.current())) {
5922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // next keyword - emit previous one
5932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    assert(typeStart == -1 || typeEnd != -1);
5942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd);
5952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (_ukeywords == null) {
5962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        _ukeywords = new HashMap<CaseInsensitiveString, String>(4);
5972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
5982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _ukeywords.put(key, type);
5992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // reset keyword info
6012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    CaseInsensitiveString tmpKey = new CaseInsensitiveString(itr.current());
6022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    key = _ukeywords.containsKey(tmpKey) ? null : tmpKey;
6032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    typeStart = typeEnd = -1;
6042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
6052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (typeStart == -1) {
6062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        typeStart = itr.currentStart();
6072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
6082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    typeEnd = itr.currentEnd();
6092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
6102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if (UnicodeLocaleExtension.isKey(itr.current())) {
6112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // 1. first keyword or
6122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // 2. next keyword, but previous one was duplicate
6132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                key = new CaseInsensitiveString(itr.current());
6142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (_ukeywords != null && _ukeywords.containsKey(key)) {
6152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // duplicate
6162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    key = null;
6172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
6182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!itr.hasNext()) {
6212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (key != null) {
6222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // last keyword
6232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    assert(typeStart == -1 || typeEnd != -1);
6242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd);
6252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (_ukeywords == null) {
6262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        _ukeywords = new HashMap<CaseInsensitiveString, String>(4);
6272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
6282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _ukeywords.put(key, type);
6292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
6302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                break;
6312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            itr.next();
6342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static class CaseInsensitiveString {
6382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private String _s;
6392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        CaseInsensitiveString(String s) {
6412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _s = s;
6422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String value() {
6452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return _s;
6462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public int hashCode() {
6492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return AsciiUtil.toLowerString(_s).hashCode();
6502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public boolean equals(Object obj) {
6532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (this == obj) {
6542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return true;
6552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!(obj instanceof CaseInsensitiveString)) {
6572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return false;
6582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return AsciiUtil.caseIgnoreMatch(_s, ((CaseInsensitiveString)obj).value());
6602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static class CaseInsensitiveChar {
6642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private char _c;
6652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        CaseInsensitiveChar(char c) {
6672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _c = c;
6682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public char value() {
6712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return _c;
6722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public int hashCode() {
6752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return AsciiUtil.toLower(_c);
6762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public boolean equals(Object obj) {
6792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (this == obj) {
6802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return true;
6812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!(obj instanceof CaseInsensitiveChar)) {
6832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return false;
6842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return _c ==  AsciiUtil.toLower(((CaseInsensitiveChar)obj).value());
6862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller}
690