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