1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2009-2010, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.impl.locale; 10 11 import java.util.Collections; 12 import java.util.Map; 13 import java.util.Map.Entry; 14 import java.util.Set; 15 import java.util.SortedMap; 16 import java.util.TreeMap; 17 import java.util.TreeSet; 18 19 import com.ibm.icu.impl.locale.InternalLocaleBuilder.CaseInsensitiveChar; 20 import com.ibm.icu.impl.locale.InternalLocaleBuilder.CaseInsensitiveString; 21 22 23 public class LocaleExtensions { 24 25 private SortedMap<Character, Extension> _map; 26 private String _id; 27 28 private static final SortedMap<Character, Extension> EMPTY_MAP = 29 Collections.unmodifiableSortedMap(new TreeMap<Character, Extension>()); 30 31 public static final LocaleExtensions EMPTY_EXTENSIONS; 32 public static final LocaleExtensions CALENDAR_JAPANESE; 33 public static final LocaleExtensions NUMBER_THAI; 34 35 static { 36 EMPTY_EXTENSIONS = new LocaleExtensions(); 37 EMPTY_EXTENSIONS._id = ""; 38 EMPTY_EXTENSIONS._map = EMPTY_MAP; 39 40 CALENDAR_JAPANESE = new LocaleExtensions(); 41 CALENDAR_JAPANESE._id = "u-ca-japanese"; 42 CALENDAR_JAPANESE._map = new TreeMap<Character, Extension>(); CALENDAR_JAPANESE._map.put(UnicodeLocaleExtension.SINGLETON, UnicodeLocaleExtension.CA_JAPANESE)43 CALENDAR_JAPANESE._map.put(UnicodeLocaleExtension.SINGLETON, UnicodeLocaleExtension.CA_JAPANESE); 44 45 NUMBER_THAI = new LocaleExtensions(); 46 NUMBER_THAI._id = "u-nu-thai"; 47 NUMBER_THAI._map = new TreeMap<Character, Extension>(); NUMBER_THAI._map.put(UnicodeLocaleExtension.SINGLETON, UnicodeLocaleExtension.NU_THAI)48 NUMBER_THAI._map.put(UnicodeLocaleExtension.SINGLETON, UnicodeLocaleExtension.NU_THAI); 49 } 50 LocaleExtensions()51 private LocaleExtensions() { 52 } 53 54 /* 55 * Package local constructor, only used by InternalLocaleBuilder. 56 */ LocaleExtensions(Map<CaseInsensitiveChar, String> extensions, Set<CaseInsensitiveString> uattributes, Map<CaseInsensitiveString, String> ukeywords)57 LocaleExtensions(Map<CaseInsensitiveChar, String> extensions, 58 Set<CaseInsensitiveString> uattributes, Map<CaseInsensitiveString, String> ukeywords) { 59 boolean hasExtension = (extensions != null && extensions.size() > 0); 60 boolean hasUAttributes = (uattributes != null && uattributes.size() > 0); 61 boolean hasUKeywords = (ukeywords != null && ukeywords.size() > 0); 62 63 if (!hasExtension && !hasUAttributes && !hasUKeywords) { 64 _map = EMPTY_MAP; 65 _id = ""; 66 return; 67 } 68 69 // Build extension map 70 _map = new TreeMap<>(); 71 if (hasExtension) { 72 for (Entry<CaseInsensitiveChar, String> ext : extensions.entrySet()) { 73 char key = AsciiUtil.toLower(ext.getKey().value()); 74 String value = ext.getValue(); 75 76 if (LanguageTag.isPrivateusePrefixChar(key)) { 77 // we need to exclude special variant in privuateuse, e.g. "x-abc-lvariant-DEF" 78 value = InternalLocaleBuilder.removePrivateuseVariant(value); 79 if (value == null) { 80 continue; 81 } 82 } 83 84 Extension e = new Extension(key, AsciiUtil.toLowerString(value)); 85 _map.put(key, e); 86 } 87 } 88 89 if (hasUAttributes || hasUKeywords) { 90 TreeSet<String> uaset = null; 91 TreeMap<String, String> ukmap = null; 92 93 if (hasUAttributes) { 94 uaset = new TreeSet<String>(); 95 for (CaseInsensitiveString cis : uattributes) { 96 uaset.add(AsciiUtil.toLowerString(cis.value())); 97 } 98 } 99 100 if (hasUKeywords) { 101 ukmap = new TreeMap<String, String>(); 102 for (Entry<CaseInsensitiveString, String> kwd : ukeywords.entrySet()) { 103 String key = AsciiUtil.toLowerString(kwd.getKey().value()); 104 String type = AsciiUtil.toLowerString(kwd.getValue()); 105 ukmap.put(key, type); 106 } 107 } 108 109 UnicodeLocaleExtension ule = new UnicodeLocaleExtension(uaset, ukmap); 110 _map.put(UnicodeLocaleExtension.SINGLETON, ule); 111 } 112 113 if (_map.size() == 0) { 114 // this could happen when only privuateuse with special variant 115 _map = EMPTY_MAP; 116 _id = ""; 117 } else { 118 _id = toID(_map); 119 } 120 } 121 getKeys()122 public Set<Character> getKeys() { 123 return Collections.unmodifiableSet(_map.keySet()); 124 } 125 getExtension(Character key)126 public Extension getExtension(Character key) { 127 return _map.get(AsciiUtil.toLower(key.charValue())); 128 } 129 getExtensionValue(Character key)130 public String getExtensionValue(Character key) { 131 Extension ext = _map.get(AsciiUtil.toLower(key.charValue())); 132 if (ext == null) { 133 return null; 134 } 135 return ext.getValue(); 136 } 137 getUnicodeLocaleAttributes()138 public Set<String> getUnicodeLocaleAttributes() { 139 Extension ext = _map.get(UnicodeLocaleExtension.SINGLETON); 140 if (ext == null) { 141 return Collections.emptySet(); 142 } 143 assert (ext instanceof UnicodeLocaleExtension); 144 return ((UnicodeLocaleExtension)ext).getUnicodeLocaleAttributes(); 145 } 146 getUnicodeLocaleKeys()147 public Set<String> getUnicodeLocaleKeys() { 148 Extension ext = _map.get(UnicodeLocaleExtension.SINGLETON); 149 if (ext == null) { 150 return Collections.emptySet(); 151 } 152 assert (ext instanceof UnicodeLocaleExtension); 153 return ((UnicodeLocaleExtension)ext).getUnicodeLocaleKeys(); 154 } 155 getUnicodeLocaleType(String unicodeLocaleKey)156 public String getUnicodeLocaleType(String unicodeLocaleKey) { 157 Extension ext = _map.get(UnicodeLocaleExtension.SINGLETON); 158 if (ext == null) { 159 return null; 160 } 161 assert (ext instanceof UnicodeLocaleExtension); 162 return ((UnicodeLocaleExtension)ext).getUnicodeLocaleType(AsciiUtil.toLowerString(unicodeLocaleKey)); 163 } 164 isEmpty()165 public boolean isEmpty() { 166 return _map.isEmpty(); 167 } 168 isValidKey(char c)169 public static boolean isValidKey(char c) { 170 return LanguageTag.isExtensionSingletonChar(c) || LanguageTag.isPrivateusePrefixChar(c); 171 } 172 isValidUnicodeLocaleKey(String ukey)173 public static boolean isValidUnicodeLocaleKey(String ukey) { 174 return UnicodeLocaleExtension.isKey(ukey); 175 } 176 toID(SortedMap<Character, Extension> map)177 private static String toID(SortedMap<Character, Extension> map) { 178 StringBuilder buf = new StringBuilder(); 179 Extension privuse = null; 180 for (Entry<Character, Extension> entry : map.entrySet()) { 181 char singleton = entry.getKey().charValue(); 182 Extension extension = entry.getValue(); 183 if (LanguageTag.isPrivateusePrefixChar(singleton)) { 184 privuse = extension; 185 } else { 186 if (buf.length() > 0) { 187 buf.append(LanguageTag.SEP); 188 } 189 buf.append(extension); 190 } 191 } 192 if (privuse != null) { 193 if (buf.length() > 0) { 194 buf.append(LanguageTag.SEP); 195 } 196 buf.append(privuse); 197 } 198 return buf.toString(); 199 } 200 201 202 @Override toString()203 public String toString() { 204 return _id; 205 } 206 getID()207 public String getID() { 208 return _id; 209 } 210 211 @Override hashCode()212 public int hashCode() { 213 return _id.hashCode(); 214 } 215 216 @Override equals(Object other)217 public boolean equals(Object other) { 218 if (this == other) { 219 return true; 220 } 221 if (!(other instanceof LocaleExtensions)) { 222 return false; 223 } 224 return this._id.equals(((LocaleExtensions)other)._id); 225 } 226 } 227