1 /* 2 * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 ******************************************************************************* 28 * Copyright (C) 2009-2010, International Business Machines Corporation and * 29 * others. All Rights Reserved. * 30 ******************************************************************************* 31 */ 32 package sun.util.locale; 33 34 import java.util.Collections; 35 import java.util.Map; 36 import java.util.Map.Entry; 37 import java.util.Set; 38 import java.util.SortedMap; 39 import java.util.SortedSet; 40 import java.util.TreeMap; 41 import java.util.TreeSet; 42 43 import sun.util.locale.InternalLocaleBuilder.CaseInsensitiveChar; 44 import sun.util.locale.InternalLocaleBuilder.CaseInsensitiveString; 45 46 47 public class LocaleExtensions { 48 49 private final Map<Character, Extension> extensionMap; 50 private final String id; 51 52 public static final LocaleExtensions CALENDAR_JAPANESE 53 = new LocaleExtensions("u-ca-japanese", 54 UnicodeLocaleExtension.SINGLETON, 55 UnicodeLocaleExtension.CA_JAPANESE); 56 57 public static final LocaleExtensions NUMBER_THAI 58 = new LocaleExtensions("u-nu-thai", 59 UnicodeLocaleExtension.SINGLETON, 60 UnicodeLocaleExtension.NU_THAI); 61 62 private LocaleExtensions(String id, Character key, Extension value) { 63 this.id = id; 64 this.extensionMap = Collections.singletonMap(key, value); 65 } 66 67 /* 68 * Package private constructor, only used by InternalLocaleBuilder. 69 */ 70 LocaleExtensions(Map<CaseInsensitiveChar, String> extensions, 71 Set<CaseInsensitiveString> uattributes, 72 Map<CaseInsensitiveString, String> ukeywords) { 73 boolean hasExtension = !LocaleUtils.isEmpty(extensions); 74 boolean hasUAttributes = !LocaleUtils.isEmpty(uattributes); 75 boolean hasUKeywords = !LocaleUtils.isEmpty(ukeywords); 76 77 if (!hasExtension && !hasUAttributes && !hasUKeywords) { 78 id = ""; 79 extensionMap = Collections.emptyMap(); 80 return; 81 } 82 83 // Build extension map 84 SortedMap<Character, Extension> map = new TreeMap<>(); 85 if (hasExtension) { 86 for (Entry<CaseInsensitiveChar, String> ext : extensions.entrySet()) { 87 char key = LocaleUtils.toLower(ext.getKey().value()); 88 String value = ext.getValue(); 89 90 if (LanguageTag.isPrivateusePrefixChar(key)) { 91 // we need to exclude special variant in privuateuse, e.g. "x-abc-lvariant-DEF" 92 value = InternalLocaleBuilder.removePrivateuseVariant(value); 93 if (value == null) { 94 continue; 95 } 96 } 97 98 map.put(key, new Extension(key, LocaleUtils.toLowerString(value))); 99 } 100 } 101 102 if (hasUAttributes || hasUKeywords) { 103 SortedSet<String> uaset = null; 104 SortedMap<String, String> ukmap = null; 105 106 if (hasUAttributes) { 107 uaset = new TreeSet<>(); 108 for (CaseInsensitiveString cis : uattributes) { 109 uaset.add(LocaleUtils.toLowerString(cis.value())); 110 } 111 } 112 113 if (hasUKeywords) { 114 ukmap = new TreeMap<>(); 115 for (Entry<CaseInsensitiveString, String> kwd : ukeywords.entrySet()) { 116 String key = LocaleUtils.toLowerString(kwd.getKey().value()); 117 String type = LocaleUtils.toLowerString(kwd.getValue()); 118 ukmap.put(key, type); 119 } 120 } 121 122 UnicodeLocaleExtension ule = new UnicodeLocaleExtension(uaset, ukmap); 123 map.put(UnicodeLocaleExtension.SINGLETON, ule); 124 } 125 126 if (map.isEmpty()) { 127 // this could happen when only privuateuse with special variant 128 id = ""; 129 extensionMap = Collections.emptyMap(); 130 } else { 131 id = toID(map); 132 extensionMap = map; 133 } 134 } 135 136 public Set<Character> getKeys() { 137 if (extensionMap.isEmpty()) { 138 return Collections.emptySet(); 139 } 140 return Collections.unmodifiableSet(extensionMap.keySet()); 141 } 142 143 public Extension getExtension(Character key) { 144 return extensionMap.get(LocaleUtils.toLower(key)); 145 } 146 147 public String getExtensionValue(Character key) { 148 Extension ext = extensionMap.get(LocaleUtils.toLower(key)); 149 if (ext == null) { 150 return null; 151 } 152 return ext.getValue(); 153 } 154 155 public Set<String> getUnicodeLocaleAttributes() { 156 Extension ext = extensionMap.get(UnicodeLocaleExtension.SINGLETON); 157 if (ext == null) { 158 return Collections.emptySet(); 159 } 160 assert (ext instanceof UnicodeLocaleExtension); 161 return ((UnicodeLocaleExtension)ext).getUnicodeLocaleAttributes(); 162 } 163 164 public Set<String> getUnicodeLocaleKeys() { 165 Extension ext = extensionMap.get(UnicodeLocaleExtension.SINGLETON); 166 if (ext == null) { 167 return Collections.emptySet(); 168 } 169 assert (ext instanceof UnicodeLocaleExtension); 170 return ((UnicodeLocaleExtension)ext).getUnicodeLocaleKeys(); 171 } 172 173 public String getUnicodeLocaleType(String unicodeLocaleKey) { 174 Extension ext = extensionMap.get(UnicodeLocaleExtension.SINGLETON); 175 if (ext == null) { 176 return null; 177 } 178 assert (ext instanceof UnicodeLocaleExtension); 179 return ((UnicodeLocaleExtension)ext).getUnicodeLocaleType(LocaleUtils.toLowerString(unicodeLocaleKey)); 180 } 181 182 public boolean isEmpty() { 183 return extensionMap.isEmpty(); 184 } 185 186 public static boolean isValidKey(char c) { 187 return LanguageTag.isExtensionSingletonChar(c) || LanguageTag.isPrivateusePrefixChar(c); 188 } 189 190 public static boolean isValidUnicodeLocaleKey(String ukey) { 191 return UnicodeLocaleExtension.isKey(ukey); 192 } 193 194 private static String toID(SortedMap<Character, Extension> map) { 195 StringBuilder buf = new StringBuilder(); 196 Extension privuse = null; 197 for (Entry<Character, Extension> entry : map.entrySet()) { 198 char singleton = entry.getKey(); 199 Extension extension = entry.getValue(); 200 if (LanguageTag.isPrivateusePrefixChar(singleton)) { 201 privuse = extension; 202 } else { 203 if (buf.length() > 0) { 204 buf.append(LanguageTag.SEP); 205 } 206 buf.append(extension); 207 } 208 } 209 if (privuse != null) { 210 if (buf.length() > 0) { 211 buf.append(LanguageTag.SEP); 212 } 213 buf.append(privuse); 214 } 215 return buf.toString(); 216 } 217 218 @Override 219 public String toString() { 220 return id; 221 } 222 223 public String getID() { 224 return id; 225 } 226 227 @Override 228 public int hashCode() { 229 return id.hashCode(); 230 } 231 232 @Override 233 public boolean equals(Object other) { 234 if (this == other) { 235 return true; 236 } 237 if (!(other instanceof LocaleExtensions)) { 238 return false; 239 } 240 return id.equals(((LocaleExtensions)other).id); 241 } 242 } 243