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.
50 private BaseLocale(String language, String region) {
51 this.language = language;
52 this.script = "";
53 this.region = region;
54 this.variant = "";
55 }
56
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.
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
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
90 public String getLanguage() {
91 return language;
92 }
93
94 public String getScript() {
95 return script;
96 }
97
98 public String getRegion() {
99 return region;
100 }
101
102 public String getVariant() {
103 return variant;
104 }
105
106 @Override
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
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
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 */
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
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;
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
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
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
272 public int hashCode() {
273 return hash;
274 }
275
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
292 public Cache() {
293 }
294
295 @Override
296 protected Key normalizeKey(Key key) {
297 return Key.normalize(key);
298 }
299
300 @Override
301 protected BaseLocale createObject(Key key) {
302 return new BaseLocale(key.lang, key.scrt, key.regn, key.vart);
303 }
304 }
305 }