1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2009-2010, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 */ 9 10 package com.ibm.icu.impl.locale; 11 12 13 public final class BaseLocale { 14 15 private static final boolean JDKIMPL = false; 16 17 public static final String SEP = "_"; 18 19 private static final Cache CACHE = new Cache(); 20 public static final BaseLocale ROOT = BaseLocale.getInstance("", "", "", ""); 21 22 private String _language = ""; 23 private String _script = ""; 24 private String _region = ""; 25 private String _variant = ""; 26 27 private transient volatile int _hash = 0; 28 29 private BaseLocale(String language, String script, String region, String variant) { 30 if (language != null) { 31 _language = AsciiUtil.toLowerString(language).intern(); 32 } 33 if (script != null) { 34 _script = AsciiUtil.toTitleString(script).intern(); 35 } 36 if (region != null) { 37 _region = AsciiUtil.toUpperString(region).intern(); 38 } 39 if (variant != null) { 40 if (JDKIMPL) { 41 // preserve upper/lower cases 42 _variant = variant.intern(); 43 } else { 44 _variant = AsciiUtil.toUpperString(variant).intern(); 45 } 46 } 47 } 48 49 public static BaseLocale getInstance(String language, String script, String region, String variant) { 50 if (JDKIMPL) { 51 // JDK uses deprecated ISO639.1 language codes for he, yi and id 52 if (AsciiUtil.caseIgnoreMatch(language, "he")) { 53 language = "iw"; 54 } else if (AsciiUtil.caseIgnoreMatch(language, "yi")) { 55 language = "ji"; 56 } else if (AsciiUtil.caseIgnoreMatch(language, "id")) { 57 language = "in"; 58 } 59 } 60 Key key = new Key(language, script, region, variant); 61 BaseLocale baseLocale = CACHE.get(key); 62 return baseLocale; 63 } 64 65 public String getLanguage() { 66 return _language; 67 } 68 69 public String getScript() { 70 return _script; 71 } 72 73 public String getRegion() { 74 return _region; 75 } 76 77 public String getVariant() { 78 return _variant; 79 } 80 81 @Override 82 public boolean equals(Object obj) { 83 if (this == obj) { 84 return true; 85 } 86 if (!(obj instanceof BaseLocale)) { 87 return false; 88 } 89 BaseLocale other = (BaseLocale)obj; 90 return hashCode() == other.hashCode() 91 && _language.equals(other._language) 92 && _script.equals(other._script) 93 && _region.equals(other._region) 94 && _variant.equals(other._variant); 95 } 96 97 @Override 98 public String toString() { 99 StringBuilder buf = new StringBuilder(); 100 if (_language.length() > 0) { 101 buf.append("language="); 102 buf.append(_language); 103 } 104 if (_script.length() > 0) { 105 if (buf.length() > 0) { 106 buf.append(", "); 107 } 108 buf.append("script="); 109 buf.append(_script); 110 } 111 if (_region.length() > 0) { 112 if (buf.length() > 0) { 113 buf.append(", "); 114 } 115 buf.append("region="); 116 buf.append(_region); 117 } 118 if (_variant.length() > 0) { 119 if (buf.length() > 0) { 120 buf.append(", "); 121 } 122 buf.append("variant="); 123 buf.append(_variant); 124 } 125 return buf.toString(); 126 } 127 128 @Override 129 public int hashCode() { 130 int h = _hash; 131 if (h == 0) { 132 // Generating a hash value from language, script, region and variant 133 for (int i = 0; i < _language.length(); i++) { 134 h = 31*h + _language.charAt(i); 135 } 136 for (int i = 0; i < _script.length(); i++) { 137 h = 31*h + _script.charAt(i); 138 } 139 for (int i = 0; i < _region.length(); i++) { 140 h = 31*h + _region.charAt(i); 141 } 142 for (int i = 0; i < _variant.length(); i++) { 143 h = 31*h + _variant.charAt(i); 144 } 145 _hash = h; 146 } 147 return h; 148 } 149 150 private static class Key implements Comparable<Key> { 151 private String _lang = ""; 152 private String _scrt = ""; 153 private String _regn = ""; 154 private String _vart = ""; 155 156 private volatile int _hash; // Default to 0 157 158 public Key(String language, String script, String region, String variant) { 159 if (language != null) { 160 _lang = language; 161 } 162 if (script != null) { 163 _scrt = script; 164 } 165 if (region != null) { 166 _regn = region; 167 } 168 if (variant != null) { 169 _vart = variant; 170 } 171 } 172 173 @Override 174 public boolean equals(Object obj) { 175 if (JDKIMPL) { 176 return (this == obj) || 177 (obj instanceof Key) 178 && AsciiUtil.caseIgnoreMatch(((Key)obj)._lang, this._lang) 179 && AsciiUtil.caseIgnoreMatch(((Key)obj)._scrt, this._scrt) 180 && AsciiUtil.caseIgnoreMatch(((Key)obj)._regn, this._regn) 181 && ((Key)obj)._vart.equals(_vart); // variant is case sensitive in JDK! 182 } 183 return (this == obj) || 184 (obj instanceof Key) 185 && AsciiUtil.caseIgnoreMatch(((Key)obj)._lang, this._lang) 186 && AsciiUtil.caseIgnoreMatch(((Key)obj)._scrt, this._scrt) 187 && AsciiUtil.caseIgnoreMatch(((Key)obj)._regn, this._regn) 188 && AsciiUtil.caseIgnoreMatch(((Key)obj)._vart, this._vart); 189 } 190 191 @Override 192 public int compareTo(Key other) { 193 int res = AsciiUtil.caseIgnoreCompare(this._lang, other._lang); 194 if (res == 0) { 195 res = AsciiUtil.caseIgnoreCompare(this._scrt, other._scrt); 196 if (res == 0) { 197 res = AsciiUtil.caseIgnoreCompare(this._regn, other._regn); 198 if (res == 0) { 199 if (JDKIMPL) { 200 res = this._vart.compareTo(other._vart); 201 } else { 202 res = AsciiUtil.caseIgnoreCompare(this._vart, other._vart); 203 } 204 } 205 } 206 } 207 return res; 208 } 209 210 @Override 211 public int hashCode() { 212 int h = _hash; 213 if (h == 0) { 214 // Generating a hash value from language, script, region and variant 215 for (int i = 0; i < _lang.length(); i++) { 216 h = 31*h + AsciiUtil.toLower(_lang.charAt(i)); 217 } 218 for (int i = 0; i < _scrt.length(); i++) { 219 h = 31*h + AsciiUtil.toLower(_scrt.charAt(i)); 220 } 221 for (int i = 0; i < _regn.length(); i++) { 222 h = 31*h + AsciiUtil.toLower(_regn.charAt(i)); 223 } 224 for (int i = 0; i < _vart.length(); i++) { 225 if (JDKIMPL) { 226 h = 31*h + _vart.charAt(i); 227 } else { 228 h = 31*h + AsciiUtil.toLower(_vart.charAt(i)); 229 } 230 } 231 _hash = h; 232 } 233 return h; 234 } 235 236 public static Key normalize(Key key) { 237 String lang = AsciiUtil.toLowerString(key._lang).intern(); 238 String scrt = AsciiUtil.toTitleString(key._scrt).intern(); 239 String regn = AsciiUtil.toUpperString(key._regn).intern(); 240 String vart; 241 if (JDKIMPL) { 242 // preserve upper/lower cases 243 vart = key._vart.intern(); 244 } else { 245 vart = AsciiUtil.toUpperString(key._vart).intern(); 246 } 247 return new Key(lang, scrt, regn, vart); 248 } 249 } 250 251 private static class Cache extends LocaleObjectCache<Key, BaseLocale> { 252 253 public Cache() { 254 } 255 256 @Override 257 protected Key normalizeKey(Key key) { 258 return Key.normalize(key); 259 } 260 261 @Override 262 protected BaseLocale createObject(Key key) { 263 return new BaseLocale(key._lang, key._scrt, key._regn, key._vart); 264 } 265 266 } 267 } 268