269 lines
9.3 KiB
Java
269 lines
9.3 KiB
Java
/*
|
|
* Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
*******************************************************************************
|
|
* Copyright (C) 2009-2010, International Business Machines Corporation and *
|
|
* others. All Rights Reserved. *
|
|
*******************************************************************************
|
|
*/
|
|
|
|
package sun.util.locale;
|
|
|
|
import jdk.internal.misc.CDS;
|
|
import jdk.internal.util.ReferencedKeySet;
|
|
import jdk.internal.util.StaticProperty;
|
|
import jdk.internal.vm.annotation.Stable;
|
|
|
|
import java.util.StringJoiner;
|
|
import java.util.function.Supplier;
|
|
|
|
public final class BaseLocale {
|
|
|
|
public static @Stable BaseLocale[] constantBaseLocales;
|
|
public static final byte ROOT = 0,
|
|
ENGLISH = 1,
|
|
US = 2,
|
|
FRENCH = 3,
|
|
GERMAN = 4,
|
|
ITALIAN = 5,
|
|
JAPANESE = 6,
|
|
KOREAN = 7,
|
|
CHINESE = 8,
|
|
SIMPLIFIED_CHINESE = 9,
|
|
TRADITIONAL_CHINESE = 10,
|
|
FRANCE = 11,
|
|
GERMANY = 12,
|
|
ITALY = 13,
|
|
JAPAN = 14,
|
|
KOREA = 15,
|
|
UK = 16,
|
|
CANADA = 17,
|
|
CANADA_FRENCH = 18,
|
|
NUM_CONSTANTS = 19;
|
|
static {
|
|
CDS.initializeFromArchive(BaseLocale.class);
|
|
BaseLocale[] baseLocales = constantBaseLocales;
|
|
if (baseLocales == null) {
|
|
baseLocales = new BaseLocale[NUM_CONSTANTS];
|
|
baseLocales[ENGLISH] = createInstance("en", "");
|
|
baseLocales[FRENCH] = createInstance("fr", "");
|
|
baseLocales[GERMAN] = createInstance("de", "");
|
|
baseLocales[ITALIAN] = createInstance("it", "");
|
|
baseLocales[JAPANESE] = createInstance("ja", "");
|
|
baseLocales[KOREAN] = createInstance("ko", "");
|
|
baseLocales[CHINESE] = createInstance("zh", "");
|
|
baseLocales[SIMPLIFIED_CHINESE] = createInstance("zh", "CN");
|
|
baseLocales[TRADITIONAL_CHINESE] = createInstance("zh", "TW");
|
|
baseLocales[FRANCE] = createInstance("fr", "FR");
|
|
baseLocales[GERMANY] = createInstance("de", "DE");
|
|
baseLocales[ITALY] = createInstance("it", "IT");
|
|
baseLocales[JAPAN] = createInstance("ja", "JP");
|
|
baseLocales[KOREA] = createInstance("ko", "KR");
|
|
baseLocales[UK] = createInstance("en", "GB");
|
|
baseLocales[US] = createInstance("en", "US");
|
|
baseLocales[CANADA] = createInstance("en", "CA");
|
|
baseLocales[CANADA_FRENCH] = createInstance("fr", "CA");
|
|
baseLocales[ROOT] = createInstance("", "");
|
|
constantBaseLocales = baseLocales;
|
|
}
|
|
}
|
|
|
|
// Interned BaseLocale cache
|
|
private static final Supplier<ReferencedKeySet<BaseLocale>> CACHE =
|
|
StableValue.supplier(new Supplier<>() {
|
|
@Override
|
|
public ReferencedKeySet<BaseLocale> get() {
|
|
return ReferencedKeySet.create(true, ReferencedKeySet.concurrentHashMapSupplier());
|
|
}
|
|
});
|
|
|
|
public static final String SEP = "_";
|
|
|
|
private final String language;
|
|
private final String script;
|
|
private final String region;
|
|
private final String variant;
|
|
|
|
private @Stable int hash;
|
|
|
|
/**
|
|
* Boolean for the old ISO language code compatibility.
|
|
* The system property "java.locale.useOldISOCodes" is not security sensitive,
|
|
* so no need to ensure privileged access here.
|
|
*/
|
|
private static final boolean OLD_ISO_CODES = StaticProperty.javaLocaleUseOldISOCodes()
|
|
.equalsIgnoreCase("true");
|
|
static {
|
|
if (OLD_ISO_CODES) {
|
|
System.err.println("WARNING: The use of the system property \"java.locale.useOldISOCodes\"" +
|
|
" is deprecated. It will be removed in a future release of the JDK.");
|
|
}
|
|
}
|
|
|
|
private BaseLocale(String language, String script, String region, String variant) {
|
|
this.language = language;
|
|
this.script = script;
|
|
this.region = region;
|
|
this.variant = variant;
|
|
}
|
|
|
|
// Called for creating the Locale.* constants. No argument
|
|
// validation is performed.
|
|
private static BaseLocale createInstance(String language, String region) {
|
|
return new BaseLocale(language, "", region, "");
|
|
}
|
|
|
|
public static BaseLocale getInstance(String language, String script,
|
|
String region, String variant) {
|
|
|
|
if (script == null) {
|
|
script = "";
|
|
}
|
|
if (region == null) {
|
|
region = "";
|
|
}
|
|
if (language == null) {
|
|
language = "";
|
|
}
|
|
if (variant == null) {
|
|
variant = "";
|
|
}
|
|
|
|
// Non-allocating for most uses
|
|
language = LocaleUtils.toLowerString(language);
|
|
region = LocaleUtils.toUpperString(region);
|
|
|
|
// Check for constant base locales first
|
|
if (script.isEmpty() && variant.isEmpty()) {
|
|
for (BaseLocale baseLocale : constantBaseLocales) {
|
|
if (baseLocale.language.equals(language)
|
|
&& baseLocale.region.equals(region)) {
|
|
return baseLocale;
|
|
}
|
|
}
|
|
}
|
|
|
|
// JDK uses deprecated ISO639.1 language codes for he, yi and id
|
|
if (!language.isEmpty()) {
|
|
language = convertOldISOCodes(language);
|
|
}
|
|
|
|
// Obtain the "interned" BaseLocale from the cache. The returned
|
|
// "interned" instance can subsequently be used by the Locale
|
|
// instance which guarantees the locale components are properly cased/interned.
|
|
return CACHE.get().intern(new BaseLocale(
|
|
language.intern(), // guaranteed to be lower-case
|
|
LocaleUtils.toTitleString(script).intern(),
|
|
region.intern(), // guaranteed to be upper-case
|
|
variant.intern()));
|
|
}
|
|
|
|
public static String convertOldISOCodes(String language) {
|
|
return switch (language) {
|
|
case "he", "iw" -> OLD_ISO_CODES ? "iw" : "he";
|
|
case "id", "in" -> OLD_ISO_CODES ? "in" : "id";
|
|
case "yi", "ji" -> OLD_ISO_CODES ? "ji" : "yi";
|
|
default -> language;
|
|
};
|
|
}
|
|
|
|
public String getLanguage() {
|
|
return language;
|
|
}
|
|
|
|
public String getScript() {
|
|
return script;
|
|
}
|
|
|
|
public String getRegion() {
|
|
return region;
|
|
}
|
|
|
|
public String getVariant() {
|
|
return variant;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
}
|
|
if (obj instanceof BaseLocale other) {
|
|
return LocaleUtils.caseIgnoreMatch(other.language, language)
|
|
&& LocaleUtils.caseIgnoreMatch(other.region, region)
|
|
&& LocaleUtils.caseIgnoreMatch(other.script, script)
|
|
// variant is case sensitive in JDK!
|
|
&& other.variant.equals(variant);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
StringJoiner sj = new StringJoiner(", ");
|
|
if (!language.isEmpty()) {
|
|
sj.add("language=" + language);
|
|
}
|
|
if (!script.isEmpty()) {
|
|
sj.add("script=" + script);
|
|
}
|
|
if (!region.isEmpty()) {
|
|
sj.add("region=" + region);
|
|
}
|
|
if (!variant.isEmpty()) {
|
|
sj.add("variant=" + variant);
|
|
}
|
|
return sj.toString();
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
int h = hash;
|
|
if (h == 0) {
|
|
int len = language.length();
|
|
for (int i = 0; i < len; i++) {
|
|
h = 31*h + LocaleUtils.toLower(language.charAt(i));
|
|
}
|
|
len = script.length();
|
|
for (int i = 0; i < len; i++) {
|
|
h = 31*h + LocaleUtils.toLower(script.charAt(i));
|
|
}
|
|
len = region.length();
|
|
for (int i = 0; i < len; i++) {
|
|
h = 31*h + LocaleUtils.toLower(region.charAt(i));
|
|
}
|
|
len = variant.length();
|
|
for (int i = 0; i < len; i++) {
|
|
h = 31*h + variant.charAt(i);
|
|
}
|
|
if (h != 0) {
|
|
hash = h;
|
|
}
|
|
}
|
|
return h;
|
|
}
|
|
}
|