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