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 33 package sun.util.locale; 34 import java.lang.ref.SoftReference; 35 36 37 public final class BaseLocale { 38 39 public static final String SEP = "_"; 40 41 private static final Cache CACHE = new Cache(); 42 43 private final String language; 44 private final String script; 45 private final String region; 46 private final String variant; 47 48 private volatile int hash = 0; 49 50 // This method must be called only when creating the Locale.* constants. BaseLocale(String language, String region)51 private BaseLocale(String language, String region) { 52 this.language = language; 53 this.script = ""; 54 this.region = region; 55 this.variant = ""; 56 } 57 BaseLocale(String language, String script, String region, String variant)58 private BaseLocale(String language, String script, String region, String variant) { 59 this.language = (language != null) ? LocaleUtils.toLowerString(language).intern() : ""; 60 this.script = (script != null) ? LocaleUtils.toTitleString(script).intern() : ""; 61 this.region = (region != null) ? LocaleUtils.toUpperString(region).intern() : ""; 62 this.variant = (variant != null) ? variant.intern() : ""; 63 } 64 65 // Called for creating the Locale.* constants. No argument 66 // validation is performed. createInstance(String language, String region)67 public static BaseLocale createInstance(String language, String region) { 68 BaseLocale base = new BaseLocale(language, region); 69 CACHE.put(new Key(language, region), base); 70 return base; 71 } 72 getInstance(String language, String script, String region, String variant)73 public static BaseLocale getInstance(String language, String script, 74 String region, String variant) { 75 // JDK uses deprecated ISO639.1 language codes for he, yi and id 76 if (language != null) { 77 if (LocaleUtils.caseIgnoreMatch(language, "he")) { 78 language = "iw"; 79 } else if (LocaleUtils.caseIgnoreMatch(language, "yi")) { 80 language = "ji"; 81 } else if (LocaleUtils.caseIgnoreMatch(language, "id")) { 82 language = "in"; 83 } 84 } 85 86 Key key = new Key(language, script, region, variant); 87 BaseLocale baseLocale = CACHE.get(key); 88 return baseLocale; 89 } 90 getLanguage()91 public String getLanguage() { 92 return language; 93 } 94 getScript()95 public String getScript() { 96 return script; 97 } 98 getRegion()99 public String getRegion() { 100 return region; 101 } 102 getVariant()103 public String getVariant() { 104 return variant; 105 } 106 107 @Override equals(Object obj)108 public boolean equals(Object obj) { 109 if (this == obj) { 110 return true; 111 } 112 if (!(obj instanceof BaseLocale)) { 113 return false; 114 } 115 BaseLocale other = (BaseLocale)obj; 116 return language == other.language 117 && script == other.script 118 && region == other.region 119 && variant == other.variant; 120 } 121 122 @Override toString()123 public String toString() { 124 StringBuilder buf = new StringBuilder(); 125 if (language.length() > 0) { 126 buf.append("language="); 127 buf.append(language); 128 } 129 if (script.length() > 0) { 130 if (buf.length() > 0) { 131 buf.append(", "); 132 } 133 buf.append("script="); 134 buf.append(script); 135 } 136 if (region.length() > 0) { 137 if (buf.length() > 0) { 138 buf.append(", "); 139 } 140 buf.append("region="); 141 buf.append(region); 142 } 143 if (variant.length() > 0) { 144 if (buf.length() > 0) { 145 buf.append(", "); 146 } 147 buf.append("variant="); 148 buf.append(variant); 149 } 150 return buf.toString(); 151 } 152 153 @Override hashCode()154 public int hashCode() { 155 int h = hash; 156 if (h == 0) { 157 // Generating a hash value from language, script, region and variant 158 h = language.hashCode(); 159 h = 31 * h + script.hashCode(); 160 h = 31 * h + region.hashCode(); 161 h = 31 * h + variant.hashCode(); 162 hash = h; 163 } 164 return h; 165 } 166 167 // BEGIN Android-added: Add a static method to clear the stale entries in Zygote 168 /** 169 * This method cleans the stale entries in BaseLocale.CACHE. This would 170 * be called in Zygote after GC but before fork, and so to avoid the 171 * cleaning of the cache to happen in child processes. 172 * 173 * @hide 174 */ cleanCache()175 public static void cleanCache() { 176 CACHE.cleanStaleEntries(); 177 } 178 // END Android-added: Add a static method to clear the stale entries in Zygote 179 180 private static final class Key { 181 private final SoftReference<String> lang; 182 private final SoftReference<String> scrt; 183 private final SoftReference<String> regn; 184 private final SoftReference<String> vart; 185 private final boolean normalized; 186 private final int hash; 187 188 /** 189 * Creates a Key. language and region must be normalized 190 * (intern'ed in the proper case). 191 */ Key(String language, String region)192 private Key(String language, String region) { 193 assert language.intern() == language 194 && region.intern() == region; 195 196 lang = new SoftReference(language); 197 scrt = new SoftReference(""); 198 regn = new SoftReference(region); 199 vart = new SoftReference(""); 200 this.normalized = true; 201 202 int h = language.hashCode(); 203 if (region != "") { 204 int len = region.length(); 205 for (int i = 0; i < len; i++) { 206 h = 31 * h + LocaleUtils.toLower(region.charAt(i)); 207 } 208 } 209 hash = h; 210 } 211 Key(String language, String script, String region, String variant)212 public Key(String language, String script, String region, String variant) { 213 this(language, script, region, variant, false); 214 } 215 Key(String language, String script, String region, String variant, boolean normalized)216 private Key(String language, String script, String region, 217 String variant, boolean normalized) { 218 int h = 0; 219 if (language != null) { 220 lang = new SoftReference(language); 221 int len = language.length(); 222 for (int i = 0; i < len; i++) { 223 h = 31*h + LocaleUtils.toLower(language.charAt(i)); 224 } 225 } else { 226 lang = new SoftReference(""); 227 } 228 if (script != null) { 229 scrt = new SoftReference(script); 230 int len = script.length(); 231 for (int i = 0; i < len; i++) { 232 h = 31*h + LocaleUtils.toLower(script.charAt(i)); 233 } 234 } else { 235 scrt = new SoftReference(""); 236 } 237 if (region != null) { 238 regn = new SoftReference(region); 239 int len = region.length(); 240 for (int i = 0; i < len; i++) { 241 h = 31*h + LocaleUtils.toLower(region.charAt(i)); 242 } 243 } else { 244 regn = new SoftReference(""); 245 } 246 if (variant != null) { 247 vart = new SoftReference(variant); 248 int len = variant.length(); 249 for (int i = 0; i < len; i++) { 250 h = 31*h + variant.charAt(i); 251 } 252 } else { 253 vart = new SoftReference(""); 254 } 255 hash = h; 256 this.normalized = normalized; 257 } 258 259 @Override equals(Object obj)260 public boolean equals(Object obj) { 261 if (this == obj) { 262 return true; 263 } 264 265 if (obj instanceof Key && this.hash == ((Key)obj).hash) { 266 String tl = this.lang.get(); 267 String ol = ((Key)obj).lang.get(); 268 if (tl != null && ol != null && 269 LocaleUtils.caseIgnoreMatch(ol, tl)) { 270 String ts = this.scrt.get(); 271 String os = ((Key)obj).scrt.get(); 272 if (ts != null && os != null && 273 LocaleUtils.caseIgnoreMatch(os, ts)) { 274 String tr = this.regn.get(); 275 String or = ((Key)obj).regn.get(); 276 if (tr != null && or != null && 277 LocaleUtils.caseIgnoreMatch(or, tr)) { 278 String tv = this.vart.get(); 279 String ov = ((Key)obj).vart.get(); 280 return (ov != null && ov.equals(tv)); 281 } 282 } 283 } 284 } 285 return false; 286 } 287 288 @Override hashCode()289 public int hashCode() { 290 return hash; 291 } 292 normalize(Key key)293 public static Key normalize(Key key) { 294 if (key.normalized) { 295 return key; 296 } 297 298 String lang = LocaleUtils.toLowerString(key.lang.get()).intern(); 299 String scrt = LocaleUtils.toTitleString(key.scrt.get()).intern(); 300 String regn = LocaleUtils.toUpperString(key.regn.get()).intern(); 301 String vart = key.vart.get().intern(); // preserve upper/lower cases 302 303 return new Key(lang, scrt, regn, vart, true); 304 } 305 } 306 307 private static class Cache extends LocaleObjectCache<Key, BaseLocale> { 308 Cache()309 public Cache() { 310 } 311 312 @Override normalizeKey(Key key)313 protected Key normalizeKey(Key key) { 314 assert key.lang.get() != null && 315 key.scrt.get() != null && 316 key.regn.get() != null && 317 key.vart.get() != null; 318 319 return Key.normalize(key); 320 } 321 322 @Override createObject(Key key)323 protected BaseLocale createObject(Key key) { 324 return new BaseLocale(key.lang.get(), key.scrt.get(), 325 key.regn.get(), key.vart.get()); 326 } 327 } 328 } 329