17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/* 27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Copyright (C) 2009-2010, International Business Machines Corporation and * 47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved. * 57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl.locale; 87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.ArrayList; 107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashMap; 117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashSet; 127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List; 137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set; 147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic final class InternalLocaleBuilder { 167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final boolean JDKIMPL = false; 187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String _language = ""; 207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String _script = ""; 217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String _region = ""; 227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String _variant = ""; 237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final CaseInsensitiveChar PRIVUSE_KEY = new CaseInsensitiveChar(LanguageTag.PRIVATEUSE.charAt(0)); 257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private HashMap<CaseInsensitiveChar, String> _extensions; 277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private HashSet<CaseInsensitiveString> _uattributes; 287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private HashMap<CaseInsensitiveString, String> _ukeywords; 297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder() { 327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder setLanguage(String language) throws LocaleSyntaxException { 357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (language == null || language.length() == 0) { 367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _language = ""; 377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!LanguageTag.isLanguage(language)) { 397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed language: " + language, 0); 407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _language = language; 427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder setScript(String script) throws LocaleSyntaxException { 477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (script == null || script.length() == 0) { 487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _script = ""; 497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!LanguageTag.isScript(script)) { 517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed script: " + script, 0); 527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _script = script; 547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder setRegion(String region) throws LocaleSyntaxException { 597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (region == null || region.length() == 0) { 607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _region = ""; 617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!LanguageTag.isRegion(region)) { 637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed region: " + region, 0); 647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _region = region; 667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder setVariant(String variant) throws LocaleSyntaxException { 717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (variant == null || variant.length() == 0) { 727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _variant = ""; 737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // normalize separators to "_" 757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String var = variant.replaceAll(LanguageTag.SEP, BaseLocale.SEP); 767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int errIdx = checkVariants(var, BaseLocale.SEP); 777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (errIdx != -1) { 787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx); 797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _variant = var; 817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder addUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException { 867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (attribute == null || !UnicodeLocaleExtension.isAttribute(attribute)) { 877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute); 887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Use case insensitive string to prevent duplication 907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_uattributes == null) { 917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes = new HashSet<CaseInsensitiveString>(4); 927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes.add(new CaseInsensitiveString(attribute)); 947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder removeUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException { 987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (attribute == null || !UnicodeLocaleExtension.isAttribute(attribute)) { 997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute); 1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_uattributes != null) { 1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes.remove(new CaseInsensitiveString(attribute)); 1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder setUnicodeLocaleKeyword(String key, String type) throws LocaleSyntaxException { 1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!UnicodeLocaleExtension.isKey(key)) { 1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed Unicode locale keyword key: " + key); 1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert CaseInsensitiveString cikey = new CaseInsensitiveString(key); 1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (type == null) { 1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_ukeywords != null) { 1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // null type is used for remove the key 1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords.remove(cikey); 1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (type.length() != 0) { 1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // normalize separator to "-" 1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String tp = type.replaceAll(BaseLocale.SEP, LanguageTag.SEP); 1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // validate 1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringTokenIterator itr = new StringTokenIterator(tp, LanguageTag.SEP); 1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String s = itr.current(); 1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!UnicodeLocaleExtension.isTypeSubtag(s)) { 1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed Unicode locale keyword type: " + type, itr.currentStart()); 1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_ukeywords == null) { 1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords = new HashMap<CaseInsensitiveString, String>(4); 1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords.put(cikey, type); 1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder setExtension(char singleton, String value) throws LocaleSyntaxException { 1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // validate key 1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert boolean isBcpPrivateuse = LanguageTag.isPrivateusePrefixChar(singleton); 1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!isBcpPrivateuse && !LanguageTag.isExtensionSingletonChar(singleton)) { 1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed extension key: " + singleton); 1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert boolean remove = (value == null || value.length() == 0); 1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert CaseInsensitiveChar key = new CaseInsensitiveChar(singleton); 1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (remove) { 1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (UnicodeLocaleExtension.isSingletonChar(key.value())) { 1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // clear entire Unicode locale extension 1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_uattributes != null) { 1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes.clear(); 1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_ukeywords != null) { 1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords.clear(); 1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_extensions != null && _extensions.containsKey(key)) { 1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions.remove(key); 1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // validate value 1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String val = value.replaceAll(BaseLocale.SEP, LanguageTag.SEP); 1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringTokenIterator itr = new StringTokenIterator(val, LanguageTag.SEP); 1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String s = itr.current(); 1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert boolean validSubtag; 1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (isBcpPrivateuse) { 1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert validSubtag = LanguageTag.isPrivateuseSubtag(s); 1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert validSubtag = LanguageTag.isExtensionSubtag(s); 1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!validSubtag) { 1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed extension value: " + s, itr.currentStart()); 1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (UnicodeLocaleExtension.isSingletonChar(key.value())) { 1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setUnicodeLocaleExtension(val); 1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_extensions == null) { 1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions = new HashMap<CaseInsensitiveChar, String>(4); 1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions.put(key, val); 1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* 1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Set extension/private subtags in a single string representation 1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder setExtensions(String subtags) throws LocaleSyntaxException { 1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (subtags == null || subtags.length() == 0) { 1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert clearExtensions(); 2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert subtags = subtags.replaceAll(BaseLocale.SEP, LanguageTag.SEP); 2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP); 2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert List<String> extensions = null; 2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String privateuse = null; 2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int parsed = 0; 2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int start; 2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Make a list of extension subtags 2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String s = itr.current(); 2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (LanguageTag.isExtensionSingleton(s)) { 2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert start = itr.currentStart(); 2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String singleton = s; 2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder sb = new StringBuilder(singleton); 2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert s = itr.current(); 2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (LanguageTag.isExtensionSubtag(s)) { 2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb.append(LanguageTag.SEP).append(s); 2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert parsed = itr.currentEnd(); 2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (parsed < start) { 2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Incomplete extension '" + singleton + "'", start); 2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (extensions == null) { 2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert extensions = new ArrayList<String>(4); 2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert extensions.add(sb.toString()); 2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!itr.isDone()) { 2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String s = itr.current(); 2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (LanguageTag.isPrivateusePrefix(s)) { 2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert start = itr.currentStart(); 2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder sb = new StringBuilder(s); 2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert s = itr.current(); 2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!LanguageTag.isPrivateuseSubtag(s)) { 2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb.append(LanguageTag.SEP).append(s); 2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert parsed = itr.currentEnd(); 2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (parsed <= start) { 2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Incomplete privateuse:" + subtags.substring(start), start); 2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert privateuse = sb.toString(); 2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!itr.isDone()) { 2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed extension subtags:" + subtags.substring(itr.currentStart()), itr.currentStart()); 2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return setExtensions(extensions, privateuse); 2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* 2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Set a list of BCP47 extensions and private use subtags 2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * BCP47 extensions are already validated and well-formed, but may contain duplicates 2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private InternalLocaleBuilder setExtensions(List<String> bcpExtensions, String privateuse) { 2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert clearExtensions(); 2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (bcpExtensions != null && bcpExtensions.size() > 0) { 2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert HashSet<CaseInsensitiveChar> processedExtensions = new HashSet<CaseInsensitiveChar>(bcpExtensions.size()); 2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (String bcpExt : bcpExtensions) { 2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert CaseInsensitiveChar key = new CaseInsensitiveChar(bcpExt.charAt(0)); 2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // ignore duplicates 2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!processedExtensions.contains(key)) { 2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // each extension string contains singleton, e.g. "a-abc-def" 2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (UnicodeLocaleExtension.isSingletonChar(key.value())) { 2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setUnicodeLocaleExtension(bcpExt.substring(2)); 2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_extensions == null) { 2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions = new HashMap<CaseInsensitiveChar, String>(4); 2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions.put(key, bcpExt.substring(2)); 2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (privateuse != null && privateuse.length() > 0) { 3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // privateuse string contains prefix, e.g. "x-abc-def" 3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_extensions == null) { 3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions = new HashMap<CaseInsensitiveChar, String>(1); 3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions.put(new CaseInsensitiveChar(privateuse.charAt(0)), privateuse.substring(2)); 3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* 3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Reset Builder's internal state with the given language tag 3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder setLanguageTag(LanguageTag langtag) { 3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert clear(); 3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (langtag.getExtlangs().size() > 0) { 3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _language = langtag.getExtlangs().get(0); 3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String language = langtag.getLanguage(); 3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!language.equals(LanguageTag.UNDETERMINED)) { 3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _language = language; 3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _script = langtag.getScript(); 3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _region = langtag.getRegion(); 3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert List<String> bcpVariants = langtag.getVariants(); 3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (bcpVariants.size() > 0) { 3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder var = new StringBuilder(bcpVariants.get(0)); 3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 1; i < bcpVariants.size(); i++) { 3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert var.append(BaseLocale.SEP).append(bcpVariants.get(i)); 3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _variant = var.toString(); 3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setExtensions(langtag.getExtensions(), langtag.getPrivateuse()); 3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder setLocale(BaseLocale base, LocaleExtensions extensions) throws LocaleSyntaxException { 3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String language = base.getLanguage(); 3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String script = base.getScript(); 3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String region = base.getRegion(); 3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String variant = base.getVariant(); 3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (JDKIMPL) { 3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Special backward compatibility support 3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Exception 1 - ja_JP_JP 3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (language.equals("ja") && region.equals("JP") && variant.equals("JP")) { 3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // When locale ja_JP_JP is created, ca-japanese is always there. 3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The builder ignores the variant "JP" 3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert("japanese".equals(extensions.getUnicodeLocaleType("ca"))); 3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert variant = ""; 3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Exception 2 - th_TH_TH 3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert else if (language.equals("th") && region.equals("TH") && variant.equals("TH")) { 3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // When locale th_TH_TH is created, nu-thai is always there. 3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The builder ignores the variant "TH" 3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert("thai".equals(extensions.getUnicodeLocaleType("nu"))); 3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert variant = ""; 3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Exception 3 - no_NO_NY 3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert else if (language.equals("no") && region.equals("NO") && variant.equals("NY")) { 3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // no_NO_NY is a valid locale and used by Java 6 or older versions. 3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The build ignores the variant "NY" and change the language to "nn". 3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert language = "nn"; 3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert variant = ""; 3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Validate base locale fields before updating internal state. 3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // LocaleExtensions always store validated/canonicalized values, 3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // so no checks are necessary. 3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (language.length() > 0 && !LanguageTag.isLanguage(language)) { 3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed language: " + language); 3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (script.length() > 0 && !LanguageTag.isScript(script)) { 3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed script: " + script); 3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (region.length() > 0 && !LanguageTag.isRegion(region)) { 3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed region: " + region); 3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (variant.length() > 0) { 3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int errIdx = checkVariants(variant, BaseLocale.SEP); 3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (errIdx != -1) { 3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx); 3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The input locale is validated at this point. 3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Now, updating builder's internal fields. 3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _language = language; 3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _script = script; 3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _region = region; 4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _variant = variant; 4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert clearExtensions(); 4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Set<Character> extKeys = (extensions == null) ? null : extensions.getKeys(); 4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (extKeys != null) { 4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // map extensions back to builder's internal format 4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (Character key : extKeys) { 4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Extension e = extensions.getExtension(key); 4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (e instanceof UnicodeLocaleExtension) { 4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert UnicodeLocaleExtension ue = (UnicodeLocaleExtension)e; 4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (String uatr : ue.getUnicodeLocaleAttributes()) { 4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_uattributes == null) { 4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes = new HashSet<CaseInsensitiveString>(4); 4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes.add(new CaseInsensitiveString(uatr)); 4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (String ukey : ue.getUnicodeLocaleKeys()) { 4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_ukeywords == null) { 4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords = new HashMap<CaseInsensitiveString, String>(4); 4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords.put(new CaseInsensitiveString(ukey), ue.getUnicodeLocaleType(ukey)); 4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_extensions == null) { 4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions = new HashMap<CaseInsensitiveChar, String>(4); 4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions.put(new CaseInsensitiveChar(key.charValue()), e.getValue()); 4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder clear() { 4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _language = ""; 4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _script = ""; 4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _region = ""; 4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _variant = ""; 4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert clearExtensions(); 4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public InternalLocaleBuilder clearExtensions() { 4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_extensions != null) { 4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _extensions.clear(); 4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_uattributes != null) { 4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes.clear(); 4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_ukeywords != null) { 4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords.clear(); 4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return this; 4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public BaseLocale getBaseLocale() { 4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String language = _language; 4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String script = _script; 4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String region = _region; 4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String variant = _variant; 4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Special private use subtag sequence identified by "lvariant" will be 4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // interpreted as Java variant. 4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_extensions != null) { 4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String privuse = _extensions.get(PRIVUSE_KEY); 4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (privuse != null) { 4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringTokenIterator itr = new StringTokenIterator(privuse, LanguageTag.SEP); 4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert boolean sawPrefix = false; 4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int privVarStart = -1; 4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (sawPrefix) { 4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert privVarStart = itr.currentStart(); 4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (AsciiUtil.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) { 4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sawPrefix = true; 4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (privVarStart != -1) { 4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder sb = new StringBuilder(variant); 4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (sb.length() != 0) { 4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb.append(BaseLocale.SEP); 4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb.append(privuse.substring(privVarStart).replaceAll(LanguageTag.SEP, BaseLocale.SEP)); 4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert variant = sb.toString(); 4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return BaseLocale.getInstance(language, script, region, variant); 4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public LocaleExtensions getLocaleExtensions() { 4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if ((_extensions == null || _extensions.size() == 0) 4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert && (_uattributes == null || _uattributes.size() == 0) 4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert && (_ukeywords == null || _ukeywords.size() == 0)) { 4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return LocaleExtensions.EMPTY_EXTENSIONS; 4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return new LocaleExtensions(_extensions, _uattributes, _ukeywords); 5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* 5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Remove special private use subtag sequence identified by "lvariant" 5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and return the rest. Only used by LocaleExtensions 5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert static String removePrivateuseVariant(String privuseVal) { 5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringTokenIterator itr = new StringTokenIterator(privuseVal, LanguageTag.SEP); 5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Note: privateuse value "abc-lvariant" is unchanged 5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // because no subtags after "lvariant". 5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int prefixStart = -1; 5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert boolean sawPrivuseVar = false; 5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (prefixStart != -1) { 5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Note: privateuse value "abc-lvariant" is unchanged 5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // because no subtags after "lvariant". 5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sawPrivuseVar = true; 5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (AsciiUtil.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) { 5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prefixStart = itr.currentStart(); 5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!sawPrivuseVar) { 5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return privuseVal; 5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert(prefixStart == 0 || prefixStart > 1); 5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return (prefixStart == 0) ? null : privuseVal.substring(0, prefixStart -1); 5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* 5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Check if the given variant subtags separated by the given 5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * separator(s) are valid 5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int checkVariants(String variants, String sep) { 5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringTokenIterator itr = new StringTokenIterator(variants, sep); 5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String s = itr.current(); 5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!LanguageTag.isVariant(s)) { 5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return itr.currentStart(); 5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return -1; 5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* 5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Private methods parsing Unicode Locale Extension subtags. 5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Duplicated attributes/keywords will be ignored. 5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The input must be a valid extension subtags (excluding singleton). 5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void setUnicodeLocaleExtension(String subtags) { 5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // wipe out existing attributes/keywords 5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_uattributes != null) { 5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes.clear(); 5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_ukeywords != null) { 5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords.clear(); 5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP); 5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // parse attributes 5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!UnicodeLocaleExtension.isAttribute(itr.current())) { 5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_uattributes == null) { 5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes = new HashSet<CaseInsensitiveString>(4); 5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _uattributes.add(new CaseInsensitiveString(itr.current())); 5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // parse keywords 5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert CaseInsensitiveString key = null; 5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String type; 5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int typeStart = -1; 5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int typeEnd = -1; 5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (!itr.isDone()) { 5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (key != null) { 5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (UnicodeLocaleExtension.isKey(itr.current())) { 5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // next keyword - emit previous one 5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert(typeStart == -1 || typeEnd != -1); 5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd); 5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_ukeywords == null) { 5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords = new HashMap<CaseInsensitiveString, String>(4); 5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords.put(key, type); 5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // reset keyword info 5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert CaseInsensitiveString tmpKey = new CaseInsensitiveString(itr.current()); 5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert key = _ukeywords.containsKey(tmpKey) ? null : tmpKey; 5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert typeStart = typeEnd = -1; 5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (typeStart == -1) { 6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert typeStart = itr.currentStart(); 6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert typeEnd = itr.currentEnd(); 6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (UnicodeLocaleExtension.isKey(itr.current())) { 6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // 1. first keyword or 6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // 2. next keyword, but previous one was duplicate 6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert key = new CaseInsensitiveString(itr.current()); 6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_ukeywords != null && _ukeywords.containsKey(key)) { 6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // duplicate 6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert key = null; 6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!itr.hasNext()) { 6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (key != null) { 6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // last keyword 6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert(typeStart == -1 || typeEnd != -1); 6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd); 6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (_ukeywords == null) { 6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords = new HashMap<CaseInsensitiveString, String>(4); 6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _ukeywords.put(key, type); 6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert itr.next(); 6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert static class CaseInsensitiveString { 6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String _s; 6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert CaseInsensitiveString(String s) { 6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _s = s; 6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String value() { 6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return _s; 6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int hashCode() { 6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return AsciiUtil.toLowerString(_s).hashCode(); 6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public boolean equals(Object obj) { 6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (this == obj) { 6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return true; 6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!(obj instanceof CaseInsensitiveString)) { 6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return AsciiUtil.caseIgnoreMatch(_s, ((CaseInsensitiveString)obj).value()); 6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert static class CaseInsensitiveChar { 6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private char _c; 6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert CaseInsensitiveChar(char c) { 6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert _c = c; 6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public char value() { 6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return _c; 6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int hashCode() { 6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return AsciiUtil.toLower(_c); 6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public boolean equals(Object obj) { 6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (this == obj) { 6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return true; 6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!(obj instanceof CaseInsensitiveChar)) { 6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return _c == AsciiUtil.toLower(((CaseInsensitiveChar)obj).value()); 6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert} 685