46 private final String region; |
46 private final String region; |
47 private final String variant; |
47 private final String variant; |
48 |
48 |
49 private volatile int hash; |
49 private volatile int hash; |
50 |
50 |
51 // This method must be called only when creating the Locale.* constants. |
51 // This method must be called with normalize = false only when creating the |
52 private BaseLocale(String language, String region) { |
52 // Locale.* constants and non-normalized BaseLocale$Keys used for lookup. |
53 this.language = language; |
53 private BaseLocale(String language, String script, String region, String variant, |
54 this.script = ""; |
54 boolean normalize) { |
55 this.region = region; |
55 if (normalize) { |
56 this.variant = ""; |
56 this.language = LocaleUtils.toLowerString(language).intern(); |
57 } |
57 this.script = LocaleUtils.toTitleString(script).intern(); |
58 |
58 this.region = LocaleUtils.toUpperString(region).intern(); |
59 private BaseLocale(String language, String script, String region, String variant) { |
59 this.variant = variant.intern(); |
60 this.language = (language != null) ? LocaleUtils.toLowerString(language).intern() : ""; |
60 } else { |
61 this.script = (script != null) ? LocaleUtils.toTitleString(script).intern() : ""; |
61 this.language = language; |
62 this.region = (region != null) ? LocaleUtils.toUpperString(region).intern() : ""; |
62 this.script = script; |
63 this.variant = (variant != null) ? variant.intern() : ""; |
63 this.region = region; |
|
64 this.variant = variant; |
|
65 } |
64 } |
66 } |
65 |
67 |
66 // Called for creating the Locale.* constants. No argument |
68 // Called for creating the Locale.* constants. No argument |
67 // validation is performed. |
69 // validation is performed. |
68 public static BaseLocale createInstance(String language, String region) { |
70 public static BaseLocale createInstance(String language, String region) { |
69 BaseLocale base = new BaseLocale(language, region); |
71 BaseLocale base = new BaseLocale(language, "", region, "", false); |
70 CACHE.put(new Key(language, region), base); |
72 CACHE.put(new Key(base), base); |
71 return base; |
73 return base; |
72 } |
74 } |
73 |
75 |
74 public static BaseLocale getInstance(String language, String script, |
76 public static BaseLocale getInstance(String language, String script, |
75 String region, String variant) { |
77 String region, String variant) { |
153 } |
155 } |
154 return h; |
156 return h; |
155 } |
157 } |
156 |
158 |
157 private static final class Key { |
159 private static final class Key { |
158 private final SoftReference<String> lang; |
160 /** |
159 private final SoftReference<String> scrt; |
161 * Keep a SoftReference to the Key data if normalized (actually used |
160 private final SoftReference<String> regn; |
162 * as a cache key) and not initialized via the constant creation path. |
161 private final SoftReference<String> vart; |
163 * |
|
164 * This allows us to avoid creating SoftReferences on lookup Keys |
|
165 * (which are short-lived) and for Locales created via |
|
166 * Locale#createConstant. |
|
167 */ |
|
168 private final SoftReference<BaseLocale> holderRef; |
|
169 private final BaseLocale holder; |
|
170 |
162 private final boolean normalized; |
171 private final boolean normalized; |
163 private final int hash; |
172 private final int hash; |
164 |
173 |
165 /** |
174 /** |
166 * Creates a Key. language and region must be normalized |
175 * Creates a Key. language and region must be normalized |
167 * (intern'ed in the proper case). |
176 * (intern'ed in the proper case). |
168 */ |
177 */ |
169 private Key(String language, String region) { |
178 private Key(BaseLocale locale) { |
170 assert language.intern() == language |
179 this.holder = locale; |
171 && region.intern() == region; |
180 this.holderRef = null; |
172 |
|
173 lang = new SoftReference<>(language); |
|
174 scrt = new SoftReference<>(""); |
|
175 regn = new SoftReference<>(region); |
|
176 vart = new SoftReference<>(""); |
|
177 this.normalized = true; |
181 this.normalized = true; |
|
182 String language = locale.getLanguage(); |
|
183 String region = locale.getRegion(); |
|
184 assert LocaleUtils.toLowerString(language).intern() == language |
|
185 && LocaleUtils.toUpperString(region).intern() == region |
|
186 && locale.getVariant() == "" |
|
187 && locale.getScript() == ""; |
178 |
188 |
179 int h = language.hashCode(); |
189 int h = language.hashCode(); |
180 if (region != "") { |
190 if (region != "") { |
181 int len = region.length(); |
191 int len = region.length(); |
182 for (int i = 0; i < len; i++) { |
192 for (int i = 0; i < len; i++) { |
184 } |
194 } |
185 } |
195 } |
186 hash = h; |
196 hash = h; |
187 } |
197 } |
188 |
198 |
189 public Key(String language, String script, String region, String variant) { |
|
190 this(language, script, region, variant, false); |
|
191 } |
|
192 |
|
193 private Key(String language, String script, String region, |
199 private Key(String language, String script, String region, |
194 String variant, boolean normalized) { |
200 String variant, boolean normalize) { |
|
201 if (language == null) { |
|
202 language = ""; |
|
203 } |
|
204 if (script == null) { |
|
205 script = ""; |
|
206 } |
|
207 if (region == null) { |
|
208 region = ""; |
|
209 } |
|
210 if (variant == null) { |
|
211 variant = ""; |
|
212 } |
|
213 |
|
214 BaseLocale locale = new BaseLocale(language, script, region, variant, normalize); |
|
215 this.normalized = normalize; |
|
216 if (normalized) { |
|
217 this.holderRef = new SoftReference<>(locale); |
|
218 this.holder = null; |
|
219 } else { |
|
220 this.holderRef = null; |
|
221 this.holder = locale; |
|
222 } |
|
223 this.hash = hashCode(locale); |
|
224 } |
|
225 |
|
226 public int hashCode() { |
|
227 return hash; |
|
228 } |
|
229 |
|
230 private int hashCode(BaseLocale locale) { |
195 int h = 0; |
231 int h = 0; |
196 if (language != null) { |
232 String lang = locale.getLanguage(); |
197 lang = new SoftReference<>(language); |
233 int len = lang.length(); |
198 int len = language.length(); |
234 for (int i = 0; i < len; i++) { |
199 for (int i = 0; i < len; i++) { |
235 h = 31*h + LocaleUtils.toLower(lang.charAt(i)); |
200 h = 31*h + LocaleUtils.toLower(language.charAt(i)); |
236 } |
201 } |
237 String scrt = locale.getScript(); |
202 } else { |
238 len = scrt.length(); |
203 lang = new SoftReference<>(""); |
239 for (int i = 0; i < len; i++) { |
204 } |
240 h = 31*h + LocaleUtils.toLower(scrt.charAt(i)); |
205 if (script != null) { |
241 } |
206 scrt = new SoftReference<>(script); |
242 String regn = locale.getRegion(); |
207 int len = script.length(); |
243 len = regn.length(); |
208 for (int i = 0; i < len; i++) { |
244 for (int i = 0; i < len; i++) { |
209 h = 31*h + LocaleUtils.toLower(script.charAt(i)); |
245 h = 31*h + LocaleUtils.toLower(regn.charAt(i)); |
210 } |
246 } |
211 } else { |
247 String vart = locale.getVariant(); |
212 scrt = new SoftReference<>(""); |
248 len = vart.length(); |
213 } |
249 for (int i = 0; i < len; i++) { |
214 if (region != null) { |
250 h = 31*h + vart.charAt(i); |
215 regn = new SoftReference<>(region); |
251 } |
216 int len = region.length(); |
252 return h; |
217 for (int i = 0; i < len; i++) { |
253 } |
218 h = 31*h + LocaleUtils.toLower(region.charAt(i)); |
254 |
219 } |
255 private BaseLocale getBaseLocale() { |
220 } else { |
256 return (holder == null) ? holderRef.get() : holder; |
221 regn = new SoftReference<>(""); |
|
222 } |
|
223 if (variant != null) { |
|
224 vart = new SoftReference<>(variant); |
|
225 int len = variant.length(); |
|
226 for (int i = 0; i < len; i++) { |
|
227 h = 31*h + variant.charAt(i); |
|
228 } |
|
229 } else { |
|
230 vart = new SoftReference<>(""); |
|
231 } |
|
232 hash = h; |
|
233 this.normalized = normalized; |
|
234 } |
257 } |
235 |
258 |
236 @Override |
259 @Override |
237 public boolean equals(Object obj) { |
260 public boolean equals(Object obj) { |
238 if (this == obj) { |
261 if (this == obj) { |
239 return true; |
262 return true; |
240 } |
263 } |
241 |
|
242 if (obj instanceof Key && this.hash == ((Key)obj).hash) { |
264 if (obj instanceof Key && this.hash == ((Key)obj).hash) { |
243 String tl = this.lang.get(); |
265 BaseLocale other = ((Key) obj).getBaseLocale(); |
244 String ol = ((Key)obj).lang.get(); |
266 BaseLocale locale = this.getBaseLocale(); |
245 if (tl != null && ol != null && |
267 if (other != null && locale != null |
246 LocaleUtils.caseIgnoreMatch(ol, tl)) { |
268 && LocaleUtils.caseIgnoreMatch(other.getLanguage(), locale.getLanguage()) |
247 String ts = this.scrt.get(); |
269 && LocaleUtils.caseIgnoreMatch(other.getScript(), locale.getScript()) |
248 String os = ((Key)obj).scrt.get(); |
270 && LocaleUtils.caseIgnoreMatch(other.getRegion(), locale.getRegion()) |
249 if (ts != null && os != null && |
271 // variant is case sensitive in JDK! |
250 LocaleUtils.caseIgnoreMatch(os, ts)) { |
272 && other.getVariant().equals(locale.getVariant())) { |
251 String tr = this.regn.get(); |
273 return true; |
252 String or = ((Key)obj).regn.get(); |
|
253 if (tr != null && or != null && |
|
254 LocaleUtils.caseIgnoreMatch(or, tr)) { |
|
255 String tv = this.vart.get(); |
|
256 String ov = ((Key)obj).vart.get(); |
|
257 return (ov != null && ov.equals(tv)); |
|
258 } |
|
259 } |
|
260 } |
274 } |
261 } |
275 } |
262 return false; |
276 return false; |
263 } |
|
264 |
|
265 @Override |
|
266 public int hashCode() { |
|
267 return hash; |
|
268 } |
277 } |
269 |
278 |
270 public static Key normalize(Key key) { |
279 public static Key normalize(Key key) { |
271 if (key.normalized) { |
280 if (key.normalized) { |
272 return key; |
281 return key; |
273 } |
282 } |
274 |
283 |
275 String lang = LocaleUtils.toLowerString(key.lang.get()).intern(); |
284 // Only normalized keys may be softly referencing the data holder |
276 String scrt = LocaleUtils.toTitleString(key.scrt.get()).intern(); |
285 assert (key.holder != null && key.holderRef == null); |
277 String regn = LocaleUtils.toUpperString(key.regn.get()).intern(); |
286 BaseLocale locale = key.holder; |
278 String vart = key.vart.get().intern(); // preserve upper/lower cases |
287 return new Key(locale.getLanguage(), locale.getScript(), |
279 |
288 locale.getRegion(), locale.getVariant(), true); |
280 return new Key(lang, scrt, regn, vart, true); |
|
281 } |
289 } |
282 } |
290 } |
283 |
291 |
284 private static class Cache extends LocaleObjectCache<Key, BaseLocale> { |
292 private static class Cache extends LocaleObjectCache<Key, BaseLocale> { |
285 |
293 |
286 public Cache() { |
294 public Cache() { |
287 } |
295 } |
288 |
296 |
289 @Override |
297 @Override |
290 protected Key normalizeKey(Key key) { |
298 protected Key normalizeKey(Key key) { |
291 assert key.lang.get() != null && |
|
292 key.scrt.get() != null && |
|
293 key.regn.get() != null && |
|
294 key.vart.get() != null; |
|
295 |
|
296 return Key.normalize(key); |
299 return Key.normalize(key); |
297 } |
300 } |
298 |
301 |
299 @Override |
302 @Override |
300 protected BaseLocale createObject(Key key) { |
303 protected BaseLocale createObject(Key key) { |
301 return new BaseLocale(key.lang.get(), key.scrt.get(), |
304 return Key.normalize(key).getBaseLocale(); |
302 key.regn.get(), key.vart.get()); |
|
303 } |
305 } |
304 } |
306 } |
305 } |
307 } |