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