• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2010, 2018, 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 import java.lang.ref.SoftReference;
36 import java.util.StringJoiner;
37 
38 public final class BaseLocale {
39 
40     public static final String SEP = "_";
41 
42     private static final Cache CACHE = new Cache();
43 
44     private final String language;
45     private final String script;
46     private final String region;
47     private final String variant;
48 
49     private volatile int hash;
50 
51     // This method must be called with normalize = false only when creating the
52     // Locale.* constants and non-normalized BaseLocale$Keys used for lookup.
BaseLocale(String language, String script, String region, String variant, boolean normalize)53     private BaseLocale(String language, String script, String region, String variant,
54                        boolean normalize) {
55         if (normalize) {
56             this.language = LocaleUtils.toLowerString(language).intern();
57             this.script = LocaleUtils.toTitleString(script).intern();
58             this.region = LocaleUtils.toUpperString(region).intern();
59             this.variant = variant.intern();
60         } else {
61             this.language = language;
62             this.script = script;
63             this.region = region;
64             this.variant = variant;
65         }
66     }
67 
68     // Called for creating the Locale.* constants. No argument
69     // validation is performed.
createInstance(String language, String region)70     public static BaseLocale createInstance(String language, String region) {
71         BaseLocale base = new BaseLocale(language, "", region, "", false);
72         CACHE.put(new Key(base), base);
73         return base;
74     }
75 
getInstance(String language, String script, String region, String variant)76     public static BaseLocale getInstance(String language, String script,
77                                          String region, String variant) {
78         // JDK uses deprecated ISO639.1 language codes for he, yi and id
79         if (language != null) {
80             if (LocaleUtils.caseIgnoreMatch(language, "he")) {
81                 language = "iw";
82             } else if (LocaleUtils.caseIgnoreMatch(language, "yi")) {
83                 language = "ji";
84             } else if (LocaleUtils.caseIgnoreMatch(language, "id")) {
85                 language = "in";
86             }
87         }
88 
89         Key key = new Key(language, script, region, variant, false);
90         BaseLocale baseLocale = CACHE.get(key);
91         return baseLocale;
92     }
93 
getLanguage()94     public String getLanguage() {
95         return language;
96     }
97 
getScript()98     public String getScript() {
99         return script;
100     }
101 
getRegion()102     public String getRegion() {
103         return region;
104     }
105 
getVariant()106     public String getVariant() {
107         return variant;
108     }
109 
110     @Override
equals(Object obj)111     public boolean equals(Object obj) {
112         if (this == obj) {
113             return true;
114         }
115         if (!(obj instanceof BaseLocale)) {
116             return false;
117         }
118         BaseLocale other = (BaseLocale)obj;
119         return language == other.language
120                && script == other.script
121                && region == other.region
122                && variant == other.variant;
123     }
124 
125     @Override
toString()126     public String toString() {
127         StringJoiner sj = new StringJoiner(", ");
128         if (!language.isEmpty()) {
129             sj.add("language=" + language);
130         }
131         if (!script.isEmpty()) {
132             sj.add("script=" + script);
133         }
134         if (!region.isEmpty()) {
135             sj.add("region=" + region);
136         }
137         if (!variant.isEmpty()) {
138             sj.add("variant=" + variant);
139         }
140         return sj.toString();
141     }
142 
143     @Override
hashCode()144     public int hashCode() {
145         int h = hash;
146         if (h == 0) {
147             // Generating a hash value from language, script, region and variant
148             h = language.hashCode();
149             h = 31 * h + script.hashCode();
150             h = 31 * h + region.hashCode();
151             h = 31 * h + variant.hashCode();
152             if (h != 0) {
153                 hash = h;
154             }
155         }
156         return h;
157     }
158 
159     // BEGIN Android-added: Add a static method to clear the stale entries in Zygote
160     /**
161      * This method cleans the stale entries in BaseLocale.CACHE.  This would
162      * be called in Zygote after GC but before fork, and so to avoid the
163      * cleaning of the cache to happen in child processes.
164      *
165      * @hide
166      */
cleanCache()167     public static void cleanCache() {
168         CACHE.cleanStaleEntries();
169     }
170     // END Android-added: Add a static method to clear the stale entries in Zygote
171 
172     private static final class Key {
173         /**
174          * Keep a SoftReference to the Key data if normalized (actually used
175          * as a cache key) and not initialized via the constant creation path.
176          *
177          * This allows us to avoid creating SoftReferences on lookup Keys
178          * (which are short-lived) and for Locales created via
179          * Locale#createConstant.
180          */
181         private final SoftReference<BaseLocale> holderRef;
182         private final BaseLocale holder;
183 
184         private final boolean normalized;
185         private final int hash;
186 
187         /**
188          * Creates a Key. language and region must be normalized
189          * (intern'ed in the proper case).
190          */
Key(BaseLocale locale)191         private Key(BaseLocale locale) {
192             this.holder = locale;
193             this.holderRef = null;
194             this.normalized = true;
195             String language = locale.getLanguage();
196             String region = locale.getRegion();
197             assert LocaleUtils.toLowerString(language).intern() == language
198                     && LocaleUtils.toUpperString(region).intern() == region
199                     && locale.getVariant() == ""
200                     && locale.getScript() == "";
201 
202             int h = language.hashCode();
203             if (region != "") {
204                 int len = region.length();
205                 for (int i = 0; i < len; i++) {
206                     h = 31 * h + LocaleUtils.toLower(region.charAt(i));
207                 }
208             }
209             hash = h;
210         }
211 
Key(String language, String script, String region, String variant, boolean normalize)212         private Key(String language, String script, String region,
213                     String variant, boolean normalize) {
214             if (language == null) {
215                 language = "";
216             }
217             if (script == null) {
218                 script = "";
219             }
220             if (region == null) {
221                 region = "";
222             }
223             if (variant == null) {
224                 variant = "";
225             }
226 
227             BaseLocale locale = new BaseLocale(language, script, region, variant, normalize);
228             this.normalized = normalize;
229             if (normalized) {
230                 this.holderRef = new SoftReference<>(locale);
231                 this.holder = null;
232             } else {
233                 this.holderRef = null;
234                 this.holder = locale;
235             }
236             this.hash = hashCode(locale);
237         }
238 
hashCode()239         public int hashCode() {
240             return hash;
241         }
242 
hashCode(BaseLocale locale)243         private int hashCode(BaseLocale locale) {
244             int h = 0;
245             String lang = locale.getLanguage();
246             int len = lang.length();
247             for (int i = 0; i < len; i++) {
248                 h = 31*h + LocaleUtils.toLower(lang.charAt(i));
249             }
250             String scrt = locale.getScript();
251             len = scrt.length();
252             for (int i = 0; i < len; i++) {
253                 h = 31*h + LocaleUtils.toLower(scrt.charAt(i));
254             }
255             String regn = locale.getRegion();
256             len = regn.length();
257             for (int i = 0; i < len; i++) {
258                 h = 31*h + LocaleUtils.toLower(regn.charAt(i));
259             }
260             String vart = locale.getVariant();
261             len = vart.length();
262             for (int i = 0; i < len; i++) {
263                 h = 31*h + vart.charAt(i);
264             }
265             return h;
266         }
267 
getBaseLocale()268         private BaseLocale getBaseLocale() {
269             return (holder == null) ? holderRef.get() : holder;
270         }
271 
272         @Override
equals(Object obj)273         public boolean equals(Object obj) {
274             if (this == obj) {
275                 return true;
276             }
277             if (obj instanceof Key && this.hash == ((Key)obj).hash) {
278                 BaseLocale other = ((Key) obj).getBaseLocale();
279                 BaseLocale locale = this.getBaseLocale();
280                 if (other != null && locale != null
281                     && LocaleUtils.caseIgnoreMatch(other.getLanguage(), locale.getLanguage())
282                     && LocaleUtils.caseIgnoreMatch(other.getScript(), locale.getScript())
283                     && LocaleUtils.caseIgnoreMatch(other.getRegion(), locale.getRegion())
284                     // variant is case sensitive in JDK!
285                     && other.getVariant().equals(locale.getVariant())) {
286                     return true;
287                 }
288             }
289             return false;
290         }
291 
normalize(Key key)292         public static Key normalize(Key key) {
293             if (key.normalized) {
294                 return key;
295             }
296 
297             // Only normalized keys may be softly referencing the data holder
298             assert (key.holder != null && key.holderRef == null);
299             BaseLocale locale = key.holder;
300             return new Key(locale.getLanguage(), locale.getScript(),
301                     locale.getRegion(), locale.getVariant(), true);
302         }
303     }
304 
305     private static class Cache extends LocaleObjectCache<Key, BaseLocale> {
306 
Cache()307         public Cache() {
308         }
309 
310         @Override
normalizeKey(Key key)311         protected Key normalizeKey(Key key) {
312             return Key.normalize(key);
313         }
314 
315         @Override
createObject(Key key)316         protected BaseLocale createObject(Key key) {
317             return Key.normalize(key).getBaseLocale();
318         }
319     }
320 }
321