Path: blob/master/src/java.base/share/classes/sun/util/locale/BaseLocale.java
41159 views
/*1* Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26*******************************************************************************27* Copyright (C) 2009-2010, International Business Machines Corporation and *28* others. All Rights Reserved. *29*******************************************************************************30*/3132package sun.util.locale;3334import jdk.internal.misc.CDS;35import jdk.internal.vm.annotation.Stable;36import sun.security.action.GetPropertyAction;3738import java.lang.ref.SoftReference;39import java.util.StringJoiner;4041public final class BaseLocale {4243public static @Stable BaseLocale[] constantBaseLocales;44public static final byte ENGLISH = 0,45FRENCH = 1,46GERMAN = 2,47ITALIAN = 3,48JAPANESE = 4,49KOREAN = 5,50CHINESE = 6,51SIMPLIFIED_CHINESE = 7,52TRADITIONAL_CHINESE = 8,53FRANCE = 9,54GERMANY = 10,55ITALY = 11,56JAPAN = 12,57KOREA = 13,58UK = 14,59US = 15,60CANADA = 16,61CANADA_FRENCH = 17,62ROOT = 18,63NUM_CONSTANTS = 19;64static {65CDS.initializeFromArchive(BaseLocale.class);66BaseLocale[] baseLocales = constantBaseLocales;67if (baseLocales == null) {68baseLocales = new BaseLocale[NUM_CONSTANTS];69baseLocales[ENGLISH] = createInstance("en", "");70baseLocales[FRENCH] = createInstance("fr", "");71baseLocales[GERMAN] = createInstance("de", "");72baseLocales[ITALIAN] = createInstance("it", "");73baseLocales[JAPANESE] = createInstance("ja", "");74baseLocales[KOREAN] = createInstance("ko", "");75baseLocales[CHINESE] = createInstance("zh", "");76baseLocales[SIMPLIFIED_CHINESE] = createInstance("zh", "CN");77baseLocales[TRADITIONAL_CHINESE] = createInstance("zh", "TW");78baseLocales[FRANCE] = createInstance("fr", "FR");79baseLocales[GERMANY] = createInstance("de", "DE");80baseLocales[ITALY] = createInstance("it", "IT");81baseLocales[JAPAN] = createInstance("ja", "JP");82baseLocales[KOREA] = createInstance("ko", "KR");83baseLocales[UK] = createInstance("en", "GB");84baseLocales[US] = createInstance("en", "US");85baseLocales[CANADA] = createInstance("en", "CA");86baseLocales[CANADA_FRENCH] = createInstance("fr", "CA");87baseLocales[ROOT] = createInstance("", "");88constantBaseLocales = baseLocales;89}90}9192public static final String SEP = "_";9394private final String language;95private final String script;96private final String region;97private final String variant;9899private volatile int hash;100101/**102* Boolean for the old ISO language code compatibility.103*/104private static final boolean OLD_ISO_CODES = GetPropertyAction.privilegedGetProperties()105.getProperty("java.locale.useOldISOCodes", "false")106.equalsIgnoreCase("true");107108// This method must be called with normalize = false only when creating the109// Locale.* constants and non-normalized BaseLocale$Keys used for lookup.110private BaseLocale(String language, String script, String region, String variant,111boolean normalize) {112if (normalize) {113this.language = LocaleUtils.toLowerString(language).intern();114this.script = LocaleUtils.toTitleString(script).intern();115this.region = LocaleUtils.toUpperString(region).intern();116this.variant = variant.intern();117} else {118this.language = language;119this.script = script;120this.region = region;121this.variant = variant;122}123}124125// Called for creating the Locale.* constants. No argument126// validation is performed.127private static BaseLocale createInstance(String language, String region) {128return new BaseLocale(language, "", region, "", false);129}130131public static BaseLocale getInstance(String language, String script,132String region, String variant) {133134if (script == null) {135script = "";136}137if (region == null) {138region = "";139}140if (language == null) {141language = null;142}143if (variant == null) {144variant = "";145}146147// Non-allocating for most uses148language = LocaleUtils.toLowerString(language);149region = LocaleUtils.toUpperString(region);150151// Check for constant base locales first152if (script.isEmpty() && variant.isEmpty()) {153for (BaseLocale baseLocale : constantBaseLocales) {154if (baseLocale.getLanguage().equals(language)155&& baseLocale.getRegion().equals(region)) {156return baseLocale;157}158}159}160161// JDK uses deprecated ISO639.1 language codes for he, yi and id162if (!language.isEmpty()) {163language = convertOldISOCodes(language);164}165166Key key = new Key(language, script, region, variant, false);167return Cache.CACHE.get(key);168}169170public static String convertOldISOCodes(String language) {171return switch (language) {172case "he", "iw" -> OLD_ISO_CODES ? "iw" : "he";173case "id", "in" -> OLD_ISO_CODES ? "in" : "id";174case "yi", "ji" -> OLD_ISO_CODES ? "ji" : "yi";175default -> language;176};177}178179public String getLanguage() {180return language;181}182183public String getScript() {184return script;185}186187public String getRegion() {188return region;189}190191public String getVariant() {192return variant;193}194195@Override196public boolean equals(Object obj) {197if (this == obj) {198return true;199}200if (!(obj instanceof BaseLocale)) {201return false;202}203BaseLocale other = (BaseLocale)obj;204return language == other.language205&& script == other.script206&& region == other.region207&& variant == other.variant;208}209210@Override211public String toString() {212StringJoiner sj = new StringJoiner(", ");213if (!language.isEmpty()) {214sj.add("language=" + language);215}216if (!script.isEmpty()) {217sj.add("script=" + script);218}219if (!region.isEmpty()) {220sj.add("region=" + region);221}222if (!variant.isEmpty()) {223sj.add("variant=" + variant);224}225return sj.toString();226}227228@Override229public int hashCode() {230int h = hash;231if (h == 0) {232// Generating a hash value from language, script, region and variant233h = language.hashCode();234h = 31 * h + script.hashCode();235h = 31 * h + region.hashCode();236h = 31 * h + variant.hashCode();237if (h != 0) {238hash = h;239}240}241return h;242}243244private static final class Key {245/**246* Keep a SoftReference to the Key data if normalized (actually used247* as a cache key) and not initialized via the constant creation path.248*249* This allows us to avoid creating SoftReferences on lookup Keys250* (which are short-lived) and for Locales created via251* Locale#createConstant.252*/253private final SoftReference<BaseLocale> holderRef;254private final BaseLocale holder;255256private final boolean normalized;257private final int hash;258259private Key(String language, String script, String region,260String variant, boolean normalize) {261BaseLocale locale = new BaseLocale(language, script, region, variant, normalize);262this.normalized = normalize;263if (normalized) {264this.holderRef = new SoftReference<>(locale);265this.holder = null;266} else {267this.holderRef = null;268this.holder = locale;269}270this.hash = hashCode(locale);271}272273public int hashCode() {274return hash;275}276277private int hashCode(BaseLocale locale) {278int h = 0;279String lang = locale.getLanguage();280int len = lang.length();281for (int i = 0; i < len; i++) {282h = 31*h + LocaleUtils.toLower(lang.charAt(i));283}284String scrt = locale.getScript();285len = scrt.length();286for (int i = 0; i < len; i++) {287h = 31*h + LocaleUtils.toLower(scrt.charAt(i));288}289String regn = locale.getRegion();290len = regn.length();291for (int i = 0; i < len; i++) {292h = 31*h + LocaleUtils.toLower(regn.charAt(i));293}294String vart = locale.getVariant();295len = vart.length();296for (int i = 0; i < len; i++) {297h = 31*h + vart.charAt(i);298}299return h;300}301302private BaseLocale getBaseLocale() {303return (holder == null) ? holderRef.get() : holder;304}305306@Override307public boolean equals(Object obj) {308if (this == obj) {309return true;310}311if (obj instanceof Key && this.hash == ((Key)obj).hash) {312BaseLocale other = ((Key) obj).getBaseLocale();313BaseLocale locale = this.getBaseLocale();314if (other != null && locale != null315&& LocaleUtils.caseIgnoreMatch(other.getLanguage(), locale.getLanguage())316&& LocaleUtils.caseIgnoreMatch(other.getScript(), locale.getScript())317&& LocaleUtils.caseIgnoreMatch(other.getRegion(), locale.getRegion())318// variant is case sensitive in JDK!319&& other.getVariant().equals(locale.getVariant())) {320return true;321}322}323return false;324}325326public static Key normalize(Key key) {327if (key.normalized) {328return key;329}330331// Only normalized keys may be softly referencing the data holder332assert (key.holder != null && key.holderRef == null);333BaseLocale locale = key.holder;334return new Key(locale.getLanguage(), locale.getScript(),335locale.getRegion(), locale.getVariant(), true);336}337}338339private static class Cache extends LocaleObjectCache<Key, BaseLocale> {340341private static final Cache CACHE = new Cache();342343public Cache() {344}345346@Override347protected Key normalizeKey(Key key) {348return Key.normalize(key);349}350351@Override352protected BaseLocale createObject(Key key) {353return Key.normalize(key).getBaseLocale();354}355}356}357358359