feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,208 @@
/*
* Copyright (c) 2010, 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, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
public final class AsciiUtil {
public static boolean caseIgnoreMatch(String s1, String s2) {
if (s1 == s2) {
return true;
}
int len = s1.length();
if (len != s2.length()) {
return false;
}
int i = 0;
while (i < len) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2 && toLower(c1) != toLower(c2)) {
break;
}
i++;
}
return (i == len);
}
public static int caseIgnoreCompare(String s1, String s2) {
if (s1 == s2) {
return 0;
}
return AsciiUtil.toLowerString(s1).compareTo(AsciiUtil.toLowerString(s2));
}
public static char toUpper(char c) {
if (c >= 'a' && c <= 'z') {
c -= 0x20;
}
return c;
}
public static char toLower(char c) {
if (c >= 'A' && c <= 'Z') {
c += 0x20;
}
return c;
}
public static String toLowerString(String s) {
int idx = 0;
for (; idx < s.length(); idx++) {
char c = s.charAt(idx);
if (c >= 'A' && c <= 'Z') {
break;
}
}
if (idx == s.length()) {
return s;
}
StringBuilder buf = new StringBuilder(s.substring(0, idx));
for (; idx < s.length(); idx++) {
buf.append(toLower(s.charAt(idx)));
}
return buf.toString();
}
public static String toUpperString(String s) {
int idx = 0;
for (; idx < s.length(); idx++) {
char c = s.charAt(idx);
if (c >= 'a' && c <= 'z') {
break;
}
}
if (idx == s.length()) {
return s;
}
StringBuilder buf = new StringBuilder(s.substring(0, idx));
for (; idx < s.length(); idx++) {
buf.append(toUpper(s.charAt(idx)));
}
return buf.toString();
}
public static String toTitleString(String s) {
if (s.length() == 0) {
return s;
}
int idx = 0;
char c = s.charAt(idx);
if (!(c >= 'a' && c <= 'z')) {
for (idx = 1; idx < s.length(); idx++) {
if (c >= 'A' && c <= 'Z') {
break;
}
}
}
if (idx == s.length()) {
return s;
}
StringBuilder buf = new StringBuilder(s.substring(0, idx));
if (idx == 0) {
buf.append(toUpper(s.charAt(idx)));
idx++;
}
for (; idx < s.length(); idx++) {
buf.append(toLower(s.charAt(idx)));
}
return buf.toString();
}
public static boolean isAlpha(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
public static boolean isAlphaString(String s) {
boolean b = true;
for (int i = 0; i < s.length(); i++) {
if (!isAlpha(s.charAt(i))) {
b = false;
break;
}
}
return b;
}
public static boolean isNumeric(char c) {
return (c >= '0' && c <= '9');
}
public static boolean isNumericString(String s) {
boolean b = true;
for (int i = 0; i < s.length(); i++) {
if (!isNumeric(s.charAt(i))) {
b = false;
break;
}
}
return b;
}
public static boolean isAlphaNumeric(char c) {
return isAlpha(c) || isNumeric(c);
}
public static boolean isAlphaNumericString(String s) {
boolean b = true;
for (int i = 0; i < s.length(); i++) {
if (!isAlphaNumeric(s.charAt(i))) {
b = false;
break;
}
}
return b;
}
public static class CaseInsensitiveKey {
private String _key;
private int _hash;
public CaseInsensitiveKey(String key) {
_key = key;
_hash = AsciiUtil.toLowerString(key).hashCode();
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof CaseInsensitiveKey) {
return AsciiUtil.caseIgnoreMatch(_key, ((CaseInsensitiveKey)o)._key);
}
return false;
}
public int hashCode() {
return _hash;
}
}
}

View File

@@ -0,0 +1,315 @@
/*
* Copyright (c) 2010, 2011, 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 java.lang.ref.SoftReference;
public final class BaseLocale {
public static final String SEP = "_";
private static final Cache CACHE = new Cache();
private final String language;
private final String script;
private final String region;
private final String variant;
private volatile int hash = 0;
// This method must be called only when creating the Locale.* constants.
private BaseLocale(String language, String region) {
this.language = language;
this.script = "";
this.region = region;
this.variant = "";
}
private BaseLocale(String language, String script, String region, String variant) {
this.language = (language != null) ? LocaleUtils.toLowerString(language).intern() : "";
this.script = (script != null) ? LocaleUtils.toTitleString(script).intern() : "";
this.region = (region != null) ? LocaleUtils.toUpperString(region).intern() : "";
this.variant = (variant != null) ? variant.intern() : "";
}
// Called for creating the Locale.* constants. No argument
// validation is performed.
public static BaseLocale createInstance(String language, String region) {
BaseLocale base = new BaseLocale(language, region);
CACHE.put(new Key(language, region), base);
return base;
}
public static BaseLocale getInstance(String language, String script,
String region, String variant) {
// JDK uses deprecated ISO639.1 language codes for he, yi and id
if (language != null) {
if (LocaleUtils.caseIgnoreMatch(language, "he")) {
language = "iw";
} else if (LocaleUtils.caseIgnoreMatch(language, "yi")) {
language = "ji";
} else if (LocaleUtils.caseIgnoreMatch(language, "id")) {
language = "in";
}
}
Key key = new Key(language, script, region, variant);
BaseLocale baseLocale = CACHE.get(key);
return baseLocale;
}
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)) {
return false;
}
BaseLocale other = (BaseLocale)obj;
return language == other.language
&& script == other.script
&& region == other.region
&& variant == other.variant;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
if (language.length() > 0) {
buf.append("language=");
buf.append(language);
}
if (script.length() > 0) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append("script=");
buf.append(script);
}
if (region.length() > 0) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append("region=");
buf.append(region);
}
if (variant.length() > 0) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append("variant=");
buf.append(variant);
}
return buf.toString();
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
// Generating a hash value from language, script, region and variant
h = language.hashCode();
h = 31 * h + script.hashCode();
h = 31 * h + region.hashCode();
h = 31 * h + variant.hashCode();
hash = h;
}
return h;
}
private static final class Key {
private final SoftReference<String> lang;
private final SoftReference<String> scrt;
private final SoftReference<String> regn;
private final SoftReference<String> vart;
private final boolean normalized;
private final int hash;
/**
* Creates a Key. language and region must be normalized
* (intern'ed in the proper case).
*/
private Key(String language, String region) {
assert language.intern() == language
&& region.intern() == region;
lang = new SoftReference(language);
scrt = new SoftReference("");
regn = new SoftReference(region);
vart = new SoftReference("");
this.normalized = true;
int h = language.hashCode();
if (region != "") {
int len = region.length();
for (int i = 0; i < len; i++) {
h = 31 * h + LocaleUtils.toLower(region.charAt(i));
}
}
hash = h;
}
public Key(String language, String script, String region, String variant) {
this(language, script, region, variant, false);
}
private Key(String language, String script, String region,
String variant, boolean normalized) {
int h = 0;
if (language != null) {
lang = new SoftReference(language);
int len = language.length();
for (int i = 0; i < len; i++) {
h = 31*h + LocaleUtils.toLower(language.charAt(i));
}
} else {
lang = new SoftReference("");
}
if (script != null) {
scrt = new SoftReference(script);
int len = script.length();
for (int i = 0; i < len; i++) {
h = 31*h + LocaleUtils.toLower(script.charAt(i));
}
} else {
scrt = new SoftReference("");
}
if (region != null) {
regn = new SoftReference(region);
int len = region.length();
for (int i = 0; i < len; i++) {
h = 31*h + LocaleUtils.toLower(region.charAt(i));
}
} else {
regn = new SoftReference("");
}
if (variant != null) {
vart = new SoftReference(variant);
int len = variant.length();
for (int i = 0; i < len; i++) {
h = 31*h + variant.charAt(i);
}
} else {
vart = new SoftReference("");
}
hash = h;
this.normalized = normalized;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Key && this.hash == ((Key)obj).hash) {
String tl = this.lang.get();
String ol = ((Key)obj).lang.get();
if (tl != null && ol != null &&
LocaleUtils.caseIgnoreMatch(ol, tl)) {
String ts = this.scrt.get();
String os = ((Key)obj).scrt.get();
if (ts != null && os != null &&
LocaleUtils.caseIgnoreMatch(os, ts)) {
String tr = this.regn.get();
String or = ((Key)obj).regn.get();
if (tr != null && or != null &&
LocaleUtils.caseIgnoreMatch(or, tr)) {
String tv = this.vart.get();
String ov = ((Key)obj).vart.get();
return (ov != null && ov.equals(tv));
}
}
}
}
return false;
}
@Override
public int hashCode() {
return hash;
}
public static Key normalize(Key key) {
if (key.normalized) {
return key;
}
String lang = LocaleUtils.toLowerString(key.lang.get()).intern();
String scrt = LocaleUtils.toTitleString(key.scrt.get()).intern();
String regn = LocaleUtils.toUpperString(key.regn.get()).intern();
String vart = key.vart.get().intern(); // preserve upper/lower cases
return new Key(lang, scrt, regn, vart, true);
}
}
private static class Cache extends LocaleObjectCache<Key, BaseLocale> {
public Cache() {
}
@Override
protected Key normalizeKey(Key key) {
assert key.lang.get() != null &&
key.scrt.get() != null &&
key.regn.get() != null &&
key.vart.get() != null;
return Key.normalize(key);
}
@Override
protected BaseLocale createObject(Key key) {
return new BaseLocale(key.lang.get(), key.scrt.get(),
key.regn.get(), key.vart.get());
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2010, 2011, 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;
class Extension {
private final char key;
private String value, id;
protected Extension(char key) {
this.key = key;
}
Extension(char key, String value) {
this.key = key;
setValue(value);
}
protected void setValue(String value) {
this.value = value;
this.id = key + LanguageTag.SEP + value;
}
public char getKey() {
return key;
}
public String getValue() {
return value;
}
public String getID() {
return id;
}
public String toString() {
return getID();
}
}

View File

@@ -0,0 +1,731 @@
/*
* Copyright (c) 2010, 2011, 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class InternalLocaleBuilder {
private static final CaseInsensitiveChar PRIVATEUSE_KEY
= new CaseInsensitiveChar(LanguageTag.PRIVATEUSE);
private String language = "";
private String script = "";
private String region = "";
private String variant = "";
private Map<CaseInsensitiveChar, String> extensions;
private Set<CaseInsensitiveString> uattributes;
private Map<CaseInsensitiveString, String> ukeywords;
public InternalLocaleBuilder() {
}
public InternalLocaleBuilder setLanguage(String language) throws LocaleSyntaxException {
if (LocaleUtils.isEmpty(language)) {
this.language = "";
} else {
if (!LanguageTag.isLanguage(language)) {
throw new LocaleSyntaxException("Ill-formed language: " + language, 0);
}
this.language = language;
}
return this;
}
public InternalLocaleBuilder setScript(String script) throws LocaleSyntaxException {
if (LocaleUtils.isEmpty(script)) {
this.script = "";
} else {
if (!LanguageTag.isScript(script)) {
throw new LocaleSyntaxException("Ill-formed script: " + script, 0);
}
this.script = script;
}
return this;
}
public InternalLocaleBuilder setRegion(String region) throws LocaleSyntaxException {
if (LocaleUtils.isEmpty(region)) {
this.region = "";
} else {
if (!LanguageTag.isRegion(region)) {
throw new LocaleSyntaxException("Ill-formed region: " + region, 0);
}
this.region = region;
}
return this;
}
public InternalLocaleBuilder setVariant(String variant) throws LocaleSyntaxException {
if (LocaleUtils.isEmpty(variant)) {
this.variant = "";
} else {
// normalize separators to "_"
String var = variant.replaceAll(LanguageTag.SEP, BaseLocale.SEP);
int errIdx = checkVariants(var, BaseLocale.SEP);
if (errIdx != -1) {
throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx);
}
this.variant = var;
}
return this;
}
public InternalLocaleBuilder addUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException {
if (!UnicodeLocaleExtension.isAttribute(attribute)) {
throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute);
}
// Use case insensitive string to prevent duplication
if (uattributes == null) {
uattributes = new HashSet<>(4);
}
uattributes.add(new CaseInsensitiveString(attribute));
return this;
}
public InternalLocaleBuilder removeUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException {
if (attribute == null || !UnicodeLocaleExtension.isAttribute(attribute)) {
throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute);
}
if (uattributes != null) {
uattributes.remove(new CaseInsensitiveString(attribute));
}
return this;
}
public InternalLocaleBuilder setUnicodeLocaleKeyword(String key, String type) throws LocaleSyntaxException {
if (!UnicodeLocaleExtension.isKey(key)) {
throw new LocaleSyntaxException("Ill-formed Unicode locale keyword key: " + key);
}
CaseInsensitiveString cikey = new CaseInsensitiveString(key);
if (type == null) {
if (ukeywords != null) {
// null type is used for remove the key
ukeywords.remove(cikey);
}
} else {
if (type.length() != 0) {
// normalize separator to "-"
String tp = type.replaceAll(BaseLocale.SEP, LanguageTag.SEP);
// validate
StringTokenIterator itr = new StringTokenIterator(tp, LanguageTag.SEP);
while (!itr.isDone()) {
String s = itr.current();
if (!UnicodeLocaleExtension.isTypeSubtag(s)) {
throw new LocaleSyntaxException("Ill-formed Unicode locale keyword type: "
+ type,
itr.currentStart());
}
itr.next();
}
}
if (ukeywords == null) {
ukeywords = new HashMap<>(4);
}
ukeywords.put(cikey, type);
}
return this;
}
public InternalLocaleBuilder setExtension(char singleton, String value) throws LocaleSyntaxException {
// validate key
boolean isBcpPrivateuse = LanguageTag.isPrivateusePrefixChar(singleton);
if (!isBcpPrivateuse && !LanguageTag.isExtensionSingletonChar(singleton)) {
throw new LocaleSyntaxException("Ill-formed extension key: " + singleton);
}
boolean remove = LocaleUtils.isEmpty(value);
CaseInsensitiveChar key = new CaseInsensitiveChar(singleton);
if (remove) {
if (UnicodeLocaleExtension.isSingletonChar(key.value())) {
// clear entire Unicode locale extension
if (uattributes != null) {
uattributes.clear();
}
if (ukeywords != null) {
ukeywords.clear();
}
} else {
if (extensions != null && extensions.containsKey(key)) {
extensions.remove(key);
}
}
} else {
// validate value
String val = value.replaceAll(BaseLocale.SEP, LanguageTag.SEP);
StringTokenIterator itr = new StringTokenIterator(val, LanguageTag.SEP);
while (!itr.isDone()) {
String s = itr.current();
boolean validSubtag;
if (isBcpPrivateuse) {
validSubtag = LanguageTag.isPrivateuseSubtag(s);
} else {
validSubtag = LanguageTag.isExtensionSubtag(s);
}
if (!validSubtag) {
throw new LocaleSyntaxException("Ill-formed extension value: " + s,
itr.currentStart());
}
itr.next();
}
if (UnicodeLocaleExtension.isSingletonChar(key.value())) {
setUnicodeLocaleExtension(val);
} else {
if (extensions == null) {
extensions = new HashMap<>(4);
}
extensions.put(key, val);
}
}
return this;
}
/*
* Set extension/private subtags in a single string representation
*/
public InternalLocaleBuilder setExtensions(String subtags) throws LocaleSyntaxException {
if (LocaleUtils.isEmpty(subtags)) {
clearExtensions();
return this;
}
subtags = subtags.replaceAll(BaseLocale.SEP, LanguageTag.SEP);
StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP);
List<String> extensions = null;
String privateuse = null;
int parsed = 0;
int start;
// Make a list of extension subtags
while (!itr.isDone()) {
String s = itr.current();
if (LanguageTag.isExtensionSingleton(s)) {
start = itr.currentStart();
String singleton = s;
StringBuilder sb = new StringBuilder(singleton);
itr.next();
while (!itr.isDone()) {
s = itr.current();
if (LanguageTag.isExtensionSubtag(s)) {
sb.append(LanguageTag.SEP).append(s);
parsed = itr.currentEnd();
} else {
break;
}
itr.next();
}
if (parsed < start) {
throw new LocaleSyntaxException("Incomplete extension '" + singleton + "'",
start);
}
if (extensions == null) {
extensions = new ArrayList<>(4);
}
extensions.add(sb.toString());
} else {
break;
}
}
if (!itr.isDone()) {
String s = itr.current();
if (LanguageTag.isPrivateusePrefix(s)) {
start = itr.currentStart();
StringBuilder sb = new StringBuilder(s);
itr.next();
while (!itr.isDone()) {
s = itr.current();
if (!LanguageTag.isPrivateuseSubtag(s)) {
break;
}
sb.append(LanguageTag.SEP).append(s);
parsed = itr.currentEnd();
itr.next();
}
if (parsed <= start) {
throw new LocaleSyntaxException("Incomplete privateuse:"
+ subtags.substring(start),
start);
} else {
privateuse = sb.toString();
}
}
}
if (!itr.isDone()) {
throw new LocaleSyntaxException("Ill-formed extension subtags:"
+ subtags.substring(itr.currentStart()),
itr.currentStart());
}
return setExtensions(extensions, privateuse);
}
/*
* Set a list of BCP47 extensions and private use subtags
* BCP47 extensions are already validated and well-formed, but may contain duplicates
*/
private InternalLocaleBuilder setExtensions(List<String> bcpExtensions, String privateuse) {
clearExtensions();
if (!LocaleUtils.isEmpty(bcpExtensions)) {
Set<CaseInsensitiveChar> done = new HashSet<>(bcpExtensions.size());
for (String bcpExt : bcpExtensions) {
CaseInsensitiveChar key = new CaseInsensitiveChar(bcpExt);
// ignore duplicates
if (!done.contains(key)) {
// each extension string contains singleton, e.g. "a-abc-def"
if (UnicodeLocaleExtension.isSingletonChar(key.value())) {
setUnicodeLocaleExtension(bcpExt.substring(2));
} else {
if (extensions == null) {
extensions = new HashMap<>(4);
}
extensions.put(key, bcpExt.substring(2));
}
}
done.add(key);
}
}
if (privateuse != null && privateuse.length() > 0) {
// privateuse string contains prefix, e.g. "x-abc-def"
if (extensions == null) {
extensions = new HashMap<>(1);
}
extensions.put(new CaseInsensitiveChar(privateuse), privateuse.substring(2));
}
return this;
}
/*
* Reset Builder's internal state with the given language tag
*/
public InternalLocaleBuilder setLanguageTag(LanguageTag langtag) {
clear();
if (!langtag.getExtlangs().isEmpty()) {
language = langtag.getExtlangs().get(0);
} else {
String lang = langtag.getLanguage();
if (!lang.equals(LanguageTag.UNDETERMINED)) {
language = lang;
}
}
script = langtag.getScript();
region = langtag.getRegion();
List<String> bcpVariants = langtag.getVariants();
if (!bcpVariants.isEmpty()) {
StringBuilder var = new StringBuilder(bcpVariants.get(0));
int size = bcpVariants.size();
for (int i = 1; i < size; i++) {
var.append(BaseLocale.SEP).append(bcpVariants.get(i));
}
variant = var.toString();
}
setExtensions(langtag.getExtensions(), langtag.getPrivateuse());
return this;
}
public InternalLocaleBuilder setLocale(BaseLocale base, LocaleExtensions localeExtensions) throws LocaleSyntaxException {
String language = base.getLanguage();
String script = base.getScript();
String region = base.getRegion();
String variant = base.getVariant();
// Special backward compatibility support
// Exception 1 - ja_JP_JP
if (language.equals("ja") && region.equals("JP") && variant.equals("JP")) {
// When locale ja_JP_JP is created, ca-japanese is always there.
// The builder ignores the variant "JP"
assert("japanese".equals(localeExtensions.getUnicodeLocaleType("ca")));
variant = "";
}
// Exception 2 - th_TH_TH
else if (language.equals("th") && region.equals("TH") && variant.equals("TH")) {
// When locale th_TH_TH is created, nu-thai is always there.
// The builder ignores the variant "TH"
assert("thai".equals(localeExtensions.getUnicodeLocaleType("nu")));
variant = "";
}
// Exception 3 - no_NO_NY
else if (language.equals("no") && region.equals("NO") && variant.equals("NY")) {
// no_NO_NY is a valid locale and used by Java 6 or older versions.
// The build ignores the variant "NY" and change the language to "nn".
language = "nn";
variant = "";
}
// Validate base locale fields before updating internal state.
// LocaleExtensions always store validated/canonicalized values,
// so no checks are necessary.
if (language.length() > 0 && !LanguageTag.isLanguage(language)) {
throw new LocaleSyntaxException("Ill-formed language: " + language);
}
if (script.length() > 0 && !LanguageTag.isScript(script)) {
throw new LocaleSyntaxException("Ill-formed script: " + script);
}
if (region.length() > 0 && !LanguageTag.isRegion(region)) {
throw new LocaleSyntaxException("Ill-formed region: " + region);
}
if (variant.length() > 0) {
int errIdx = checkVariants(variant, BaseLocale.SEP);
if (errIdx != -1) {
throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx);
}
}
// The input locale is validated at this point.
// Now, updating builder's internal fields.
this.language = language;
this.script = script;
this.region = region;
this.variant = variant;
clearExtensions();
Set<Character> extKeys = (localeExtensions == null) ? null : localeExtensions.getKeys();
if (extKeys != null) {
// map localeExtensions back to builder's internal format
for (Character key : extKeys) {
Extension e = localeExtensions.getExtension(key);
if (e instanceof UnicodeLocaleExtension) {
UnicodeLocaleExtension ue = (UnicodeLocaleExtension)e;
for (String uatr : ue.getUnicodeLocaleAttributes()) {
if (uattributes == null) {
uattributes = new HashSet<>(4);
}
uattributes.add(new CaseInsensitiveString(uatr));
}
for (String ukey : ue.getUnicodeLocaleKeys()) {
if (ukeywords == null) {
ukeywords = new HashMap<>(4);
}
ukeywords.put(new CaseInsensitiveString(ukey), ue.getUnicodeLocaleType(ukey));
}
} else {
if (extensions == null) {
extensions = new HashMap<>(4);
}
extensions.put(new CaseInsensitiveChar(key), e.getValue());
}
}
}
return this;
}
public InternalLocaleBuilder clear() {
language = "";
script = "";
region = "";
variant = "";
clearExtensions();
return this;
}
public InternalLocaleBuilder clearExtensions() {
if (extensions != null) {
extensions.clear();
}
if (uattributes != null) {
uattributes.clear();
}
if (ukeywords != null) {
ukeywords.clear();
}
return this;
}
public BaseLocale getBaseLocale() {
String language = this.language;
String script = this.script;
String region = this.region;
String variant = this.variant;
// Special private use subtag sequence identified by "lvariant" will be
// interpreted as Java variant.
if (extensions != null) {
String privuse = extensions.get(PRIVATEUSE_KEY);
if (privuse != null) {
StringTokenIterator itr = new StringTokenIterator(privuse, LanguageTag.SEP);
boolean sawPrefix = false;
int privVarStart = -1;
while (!itr.isDone()) {
if (sawPrefix) {
privVarStart = itr.currentStart();
break;
}
if (LocaleUtils.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) {
sawPrefix = true;
}
itr.next();
}
if (privVarStart != -1) {
StringBuilder sb = new StringBuilder(variant);
if (sb.length() != 0) {
sb.append(BaseLocale.SEP);
}
sb.append(privuse.substring(privVarStart).replaceAll(LanguageTag.SEP,
BaseLocale.SEP));
variant = sb.toString();
}
}
}
return BaseLocale.getInstance(language, script, region, variant);
}
public LocaleExtensions getLocaleExtensions() {
if (LocaleUtils.isEmpty(extensions) && LocaleUtils.isEmpty(uattributes)
&& LocaleUtils.isEmpty(ukeywords)) {
return null;
}
LocaleExtensions lext = new LocaleExtensions(extensions, uattributes, ukeywords);
return lext.isEmpty() ? null : lext;
}
/*
* Remove special private use subtag sequence identified by "lvariant"
* and return the rest. Only used by LocaleExtensions
*/
static String removePrivateuseVariant(String privuseVal) {
StringTokenIterator itr = new StringTokenIterator(privuseVal, LanguageTag.SEP);
// Note: privateuse value "abc-lvariant" is unchanged
// because no subtags after "lvariant".
int prefixStart = -1;
boolean sawPrivuseVar = false;
while (!itr.isDone()) {
if (prefixStart != -1) {
// Note: privateuse value "abc-lvariant" is unchanged
// because no subtags after "lvariant".
sawPrivuseVar = true;
break;
}
if (LocaleUtils.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) {
prefixStart = itr.currentStart();
}
itr.next();
}
if (!sawPrivuseVar) {
return privuseVal;
}
assert(prefixStart == 0 || prefixStart > 1);
return (prefixStart == 0) ? null : privuseVal.substring(0, prefixStart -1);
}
/*
* Check if the given variant subtags separated by the given
* separator(s) are valid
*/
private int checkVariants(String variants, String sep) {
StringTokenIterator itr = new StringTokenIterator(variants, sep);
while (!itr.isDone()) {
String s = itr.current();
if (!LanguageTag.isVariant(s)) {
return itr.currentStart();
}
itr.next();
}
return -1;
}
/*
* Private methods parsing Unicode Locale Extension subtags.
* Duplicated attributes/keywords will be ignored.
* The input must be a valid extension subtags (excluding singleton).
*/
private void setUnicodeLocaleExtension(String subtags) {
// wipe out existing attributes/keywords
if (uattributes != null) {
uattributes.clear();
}
if (ukeywords != null) {
ukeywords.clear();
}
StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP);
// parse attributes
while (!itr.isDone()) {
if (!UnicodeLocaleExtension.isAttribute(itr.current())) {
break;
}
if (uattributes == null) {
uattributes = new HashSet<>(4);
}
uattributes.add(new CaseInsensitiveString(itr.current()));
itr.next();
}
// parse keywords
CaseInsensitiveString key = null;
String type;
int typeStart = -1;
int typeEnd = -1;
while (!itr.isDone()) {
if (key != null) {
if (UnicodeLocaleExtension.isKey(itr.current())) {
// next keyword - emit previous one
assert(typeStart == -1 || typeEnd != -1);
type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd);
if (ukeywords == null) {
ukeywords = new HashMap<>(4);
}
ukeywords.put(key, type);
// reset keyword info
CaseInsensitiveString tmpKey = new CaseInsensitiveString(itr.current());
key = ukeywords.containsKey(tmpKey) ? null : tmpKey;
typeStart = typeEnd = -1;
} else {
if (typeStart == -1) {
typeStart = itr.currentStart();
}
typeEnd = itr.currentEnd();
}
} else if (UnicodeLocaleExtension.isKey(itr.current())) {
// 1. first keyword or
// 2. next keyword, but previous one was duplicate
key = new CaseInsensitiveString(itr.current());
if (ukeywords != null && ukeywords.containsKey(key)) {
// duplicate
key = null;
}
}
if (!itr.hasNext()) {
if (key != null) {
// last keyword
assert(typeStart == -1 || typeEnd != -1);
type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd);
if (ukeywords == null) {
ukeywords = new HashMap<>(4);
}
ukeywords.put(key, type);
}
break;
}
itr.next();
}
}
static final class CaseInsensitiveString {
private final String str, lowerStr;
CaseInsensitiveString(String s) {
str = s;
lowerStr = LocaleUtils.toLowerString(s);
}
public String value() {
return str;
}
@Override
public int hashCode() {
return lowerStr.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof CaseInsensitiveString)) {
return false;
}
return lowerStr.equals(((CaseInsensitiveString)obj).lowerStr);
}
}
static final class CaseInsensitiveChar {
private final char ch, lowerCh;
/**
* Constructs a CaseInsensitiveChar with the first char of the
* given s.
*/
private CaseInsensitiveChar(String s) {
this(s.charAt(0));
}
CaseInsensitiveChar(char c) {
ch = c;
lowerCh = LocaleUtils.toLower(ch);
}
public char value() {
return ch;
}
@Override
public int hashCode() {
return lowerCh;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof CaseInsensitiveChar)) {
return false;
}
return lowerCh == ((CaseInsensitiveChar)obj).lowerCh;
}
}
}

View File

@@ -0,0 +1,749 @@
/*
* Copyright (c) 2010, 2013, 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) 2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class LanguageTag {
//
// static fields
//
public static final String SEP = "-";
public static final String PRIVATEUSE = "x";
public static final String UNDETERMINED = "und";
public static final String PRIVUSE_VARIANT_PREFIX = "lvariant";
//
// Language subtag fields
//
private String language = ""; // language subtag
private String script = ""; // script subtag
private String region = ""; // region subtag
private String privateuse = ""; // privateuse
private List<String> extlangs = Collections.emptyList(); // extlang subtags
private List<String> variants = Collections.emptyList(); // variant subtags
private List<String> extensions = Collections.emptyList(); // extensions
// Map contains grandfathered tags and its preferred mappings from
// http://www.ietf.org/rfc/rfc5646.txt
// Keys are lower-case strings.
private static final Map<String, String[]> GRANDFATHERED = new HashMap<>();
static {
// grandfathered = irregular ; non-redundant tags registered
// / regular ; during the RFC 3066 era
//
// irregular = "en-GB-oed" ; irregular tags do not match
// / "i-ami" ; the 'langtag' production and
// / "i-bnn" ; would not otherwise be
// / "i-default" ; considered 'well-formed'
// / "i-enochian" ; These tags are all valid,
// / "i-hak" ; but most are deprecated
// / "i-klingon" ; in favor of more modern
// / "i-lux" ; subtags or subtag
// / "i-mingo" ; combination
// / "i-navajo"
// / "i-pwn"
// / "i-tao"
// / "i-tay"
// / "i-tsu"
// / "sgn-BE-FR"
// / "sgn-BE-NL"
// / "sgn-CH-DE"
//
// regular = "art-lojban" ; these tags match the 'langtag'
// / "cel-gaulish" ; production, but their subtags
// / "no-bok" ; are not extended language
// / "no-nyn" ; or variant subtags: their meaning
// / "zh-guoyu" ; is defined by their registration
// / "zh-hakka" ; and all of these are deprecated
// / "zh-min" ; in favor of a more modern
// / "zh-min-nan" ; subtag or sequence of subtags
// / "zh-xiang"
final String[][] entries = {
//{"tag", "preferred"},
{"art-lojban", "jbo"},
{"cel-gaulish", "xtg-x-cel-gaulish"}, // fallback
{"en-GB-oed", "en-GB-x-oed"}, // fallback
{"i-ami", "ami"},
{"i-bnn", "bnn"},
{"i-default", "en-x-i-default"}, // fallback
{"i-enochian", "und-x-i-enochian"}, // fallback
{"i-hak", "hak"},
{"i-klingon", "tlh"},
{"i-lux", "lb"},
{"i-mingo", "see-x-i-mingo"}, // fallback
{"i-navajo", "nv"},
{"i-pwn", "pwn"},
{"i-tao", "tao"},
{"i-tay", "tay"},
{"i-tsu", "tsu"},
{"no-bok", "nb"},
{"no-nyn", "nn"},
{"sgn-BE-FR", "sfb"},
{"sgn-BE-NL", "vgt"},
{"sgn-CH-DE", "sgg"},
{"zh-guoyu", "cmn"},
{"zh-hakka", "hak"},
{"zh-min", "nan-x-zh-min"}, // fallback
{"zh-min-nan", "nan"},
{"zh-xiang", "hsn"},
};
for (String[] e : entries) {
GRANDFATHERED.put(LocaleUtils.toLowerString(e[0]), e);
}
}
private LanguageTag() {
}
/*
* BNF in RFC5646
*
* Language-Tag = langtag ; normal language tags
* / privateuse ; private use tag
* / grandfathered ; grandfathered tags
*
*
* langtag = language
* ["-" script]
* ["-" region]
* *("-" variant)
* *("-" extension)
* ["-" privateuse]
*
* language = 2*3ALPHA ; shortest ISO 639 code
* ["-" extlang] ; sometimes followed by
* ; extended language subtags
* / 4ALPHA ; or reserved for future use
* / 5*8ALPHA ; or registered language subtag
*
* extlang = 3ALPHA ; selected ISO 639 codes
* *2("-" 3ALPHA) ; permanently reserved
*
* script = 4ALPHA ; ISO 15924 code
*
* region = 2ALPHA ; ISO 3166-1 code
* / 3DIGIT ; UN M.49 code
*
* variant = 5*8alphanum ; registered variants
* / (DIGIT 3alphanum)
*
* extension = singleton 1*("-" (2*8alphanum))
*
* ; Single alphanumerics
* ; "x" reserved for private use
* singleton = DIGIT ; 0 - 9
* / %x41-57 ; A - W
* / %x59-5A ; Y - Z
* / %x61-77 ; a - w
* / %x79-7A ; y - z
*
* privateuse = "x" 1*("-" (1*8alphanum))
*
*/
public static LanguageTag parse(String languageTag, ParseStatus sts) {
if (sts == null) {
sts = new ParseStatus();
} else {
sts.reset();
}
StringTokenIterator itr;
// Check if the tag is grandfathered
String[] gfmap = GRANDFATHERED.get(LocaleUtils.toLowerString(languageTag));
if (gfmap != null) {
// use preferred mapping
itr = new StringTokenIterator(gfmap[1], SEP);
} else {
itr = new StringTokenIterator(languageTag, SEP);
}
LanguageTag tag = new LanguageTag();
// langtag must start with either language or privateuse
if (tag.parseLanguage(itr, sts)) {
tag.parseExtlangs(itr, sts);
tag.parseScript(itr, sts);
tag.parseRegion(itr, sts);
tag.parseVariants(itr, sts);
tag.parseExtensions(itr, sts);
}
tag.parsePrivateuse(itr, sts);
if (!itr.isDone() && !sts.isError()) {
String s = itr.current();
sts.errorIndex = itr.currentStart();
if (s.length() == 0) {
sts.errorMsg = "Empty subtag";
} else {
sts.errorMsg = "Invalid subtag: " + s;
}
}
return tag;
}
//
// Language subtag parsers
//
private boolean parseLanguage(StringTokenIterator itr, ParseStatus sts) {
if (itr.isDone() || sts.isError()) {
return false;
}
boolean found = false;
String s = itr.current();
if (isLanguage(s)) {
found = true;
language = s;
sts.parseLength = itr.currentEnd();
itr.next();
}
return found;
}
private boolean parseExtlangs(StringTokenIterator itr, ParseStatus sts) {
if (itr.isDone() || sts.isError()) {
return false;
}
boolean found = false;
while (!itr.isDone()) {
String s = itr.current();
if (!isExtlang(s)) {
break;
}
found = true;
if (extlangs.isEmpty()) {
extlangs = new ArrayList<>(3);
}
extlangs.add(s);
sts.parseLength = itr.currentEnd();
itr.next();
if (extlangs.size() == 3) {
// Maximum 3 extlangs
break;
}
}
return found;
}
private boolean parseScript(StringTokenIterator itr, ParseStatus sts) {
if (itr.isDone() || sts.isError()) {
return false;
}
boolean found = false;
String s = itr.current();
if (isScript(s)) {
found = true;
script = s;
sts.parseLength = itr.currentEnd();
itr.next();
}
return found;
}
private boolean parseRegion(StringTokenIterator itr, ParseStatus sts) {
if (itr.isDone() || sts.isError()) {
return false;
}
boolean found = false;
String s = itr.current();
if (isRegion(s)) {
found = true;
region = s;
sts.parseLength = itr.currentEnd();
itr.next();
}
return found;
}
private boolean parseVariants(StringTokenIterator itr, ParseStatus sts) {
if (itr.isDone() || sts.isError()) {
return false;
}
boolean found = false;
while (!itr.isDone()) {
String s = itr.current();
if (!isVariant(s)) {
break;
}
found = true;
if (variants.isEmpty()) {
variants = new ArrayList<>(3);
}
variants.add(s);
sts.parseLength = itr.currentEnd();
itr.next();
}
return found;
}
private boolean parseExtensions(StringTokenIterator itr, ParseStatus sts) {
if (itr.isDone() || sts.isError()) {
return false;
}
boolean found = false;
while (!itr.isDone()) {
String s = itr.current();
if (isExtensionSingleton(s)) {
int start = itr.currentStart();
String singleton = s;
StringBuilder sb = new StringBuilder(singleton);
itr.next();
while (!itr.isDone()) {
s = itr.current();
if (isExtensionSubtag(s)) {
sb.append(SEP).append(s);
sts.parseLength = itr.currentEnd();
} else {
break;
}
itr.next();
}
if (sts.parseLength <= start) {
sts.errorIndex = start;
sts.errorMsg = "Incomplete extension '" + singleton + "'";
break;
}
if (extensions.isEmpty()) {
extensions = new ArrayList<>(4);
}
extensions.add(sb.toString());
found = true;
} else {
break;
}
}
return found;
}
private boolean parsePrivateuse(StringTokenIterator itr, ParseStatus sts) {
if (itr.isDone() || sts.isError()) {
return false;
}
boolean found = false;
String s = itr.current();
if (isPrivateusePrefix(s)) {
int start = itr.currentStart();
StringBuilder sb = new StringBuilder(s);
itr.next();
while (!itr.isDone()) {
s = itr.current();
if (!isPrivateuseSubtag(s)) {
break;
}
sb.append(SEP).append(s);
sts.parseLength = itr.currentEnd();
itr.next();
}
if (sts.parseLength <= start) {
// need at least 1 private subtag
sts.errorIndex = start;
sts.errorMsg = "Incomplete privateuse";
} else {
privateuse = sb.toString();
found = true;
}
}
return found;
}
public static LanguageTag parseLocale(BaseLocale baseLocale, LocaleExtensions localeExtensions) {
LanguageTag tag = new LanguageTag();
String language = baseLocale.getLanguage();
String script = baseLocale.getScript();
String region = baseLocale.getRegion();
String variant = baseLocale.getVariant();
boolean hasSubtag = false;
String privuseVar = null; // store ill-formed variant subtags
if (isLanguage(language)) {
// Convert a deprecated language code to its new code
if (language.equals("iw")) {
language = "he";
} else if (language.equals("ji")) {
language = "yi";
} else if (language.equals("in")) {
language = "id";
}
tag.language = language;
}
if (isScript(script)) {
tag.script = canonicalizeScript(script);
hasSubtag = true;
}
if (isRegion(region)) {
tag.region = canonicalizeRegion(region);
hasSubtag = true;
}
// Special handling for no_NO_NY - use nn_NO for language tag
if (tag.language.equals("no") && tag.region.equals("NO") && variant.equals("NY")) {
tag.language = "nn";
variant = "";
}
if (variant.length() > 0) {
List<String> variants = null;
StringTokenIterator varitr = new StringTokenIterator(variant, BaseLocale.SEP);
while (!varitr.isDone()) {
String var = varitr.current();
if (!isVariant(var)) {
break;
}
if (variants == null) {
variants = new ArrayList<>();
}
variants.add(var); // Do not canonicalize!
varitr.next();
}
if (variants != null) {
tag.variants = variants;
hasSubtag = true;
}
if (!varitr.isDone()) {
// ill-formed variant subtags
StringBuilder buf = new StringBuilder();
while (!varitr.isDone()) {
String prvv = varitr.current();
if (!isPrivateuseSubtag(prvv)) {
// cannot use private use subtag - truncated
break;
}
if (buf.length() > 0) {
buf.append(SEP);
}
buf.append(prvv);
varitr.next();
}
if (buf.length() > 0) {
privuseVar = buf.toString();
}
}
}
List<String> extensions = null;
String privateuse = null;
if (localeExtensions != null) {
Set<Character> locextKeys = localeExtensions.getKeys();
for (Character locextKey : locextKeys) {
Extension ext = localeExtensions.getExtension(locextKey);
if (isPrivateusePrefixChar(locextKey)) {
privateuse = ext.getValue();
} else {
if (extensions == null) {
extensions = new ArrayList<>();
}
extensions.add(locextKey.toString() + SEP + ext.getValue());
}
}
}
if (extensions != null) {
tag.extensions = extensions;
hasSubtag = true;
}
// append ill-formed variant subtags to private use
if (privuseVar != null) {
if (privateuse == null) {
privateuse = PRIVUSE_VARIANT_PREFIX + SEP + privuseVar;
} else {
privateuse = privateuse + SEP + PRIVUSE_VARIANT_PREFIX
+ SEP + privuseVar.replace(BaseLocale.SEP, SEP);
}
}
if (privateuse != null) {
tag.privateuse = privateuse;
}
if (tag.language.length() == 0 && (hasSubtag || privateuse == null)) {
// use lang "und" when 1) no language is available AND
// 2) any of other subtags other than private use are available or
// no private use tag is available
tag.language = UNDETERMINED;
}
return tag;
}
//
// Getter methods for language subtag fields
//
public String getLanguage() {
return language;
}
public List<String> getExtlangs() {
if (extlangs.isEmpty()) {
return Collections.emptyList();
}
return Collections.unmodifiableList(extlangs);
}
public String getScript() {
return script;
}
public String getRegion() {
return region;
}
public List<String> getVariants() {
if (variants.isEmpty()) {
return Collections.emptyList();
}
return Collections.unmodifiableList(variants);
}
public List<String> getExtensions() {
if (extensions.isEmpty()) {
return Collections.emptyList();
}
return Collections.unmodifiableList(extensions);
}
public String getPrivateuse() {
return privateuse;
}
//
// Language subtag syntax checking methods
//
public static boolean isLanguage(String s) {
// language = 2*3ALPHA ; shortest ISO 639 code
// ["-" extlang] ; sometimes followed by
// ; extended language subtags
// / 4ALPHA ; or reserved for future use
// / 5*8ALPHA ; or registered language subtag
int len = s.length();
return (len >= 2) && (len <= 8) && LocaleUtils.isAlphaString(s);
}
public static boolean isExtlang(String s) {
// extlang = 3ALPHA ; selected ISO 639 codes
// *2("-" 3ALPHA) ; permanently reserved
return (s.length() == 3) && LocaleUtils.isAlphaString(s);
}
public static boolean isScript(String s) {
// script = 4ALPHA ; ISO 15924 code
return (s.length() == 4) && LocaleUtils.isAlphaString(s);
}
public static boolean isRegion(String s) {
// region = 2ALPHA ; ISO 3166-1 code
// / 3DIGIT ; UN M.49 code
return ((s.length() == 2) && LocaleUtils.isAlphaString(s))
|| ((s.length() == 3) && LocaleUtils.isNumericString(s));
}
public static boolean isVariant(String s) {
// variant = 5*8alphanum ; registered variants
// / (DIGIT 3alphanum)
int len = s.length();
if (len >= 5 && len <= 8) {
return LocaleUtils.isAlphaNumericString(s);
}
if (len == 4) {
return LocaleUtils.isNumeric(s.charAt(0))
&& LocaleUtils.isAlphaNumeric(s.charAt(1))
&& LocaleUtils.isAlphaNumeric(s.charAt(2))
&& LocaleUtils.isAlphaNumeric(s.charAt(3));
}
return false;
}
public static boolean isExtensionSingleton(String s) {
// singleton = DIGIT ; 0 - 9
// / %x41-57 ; A - W
// / %x59-5A ; Y - Z
// / %x61-77 ; a - w
// / %x79-7A ; y - z
return (s.length() == 1)
&& LocaleUtils.isAlphaString(s)
&& !LocaleUtils.caseIgnoreMatch(PRIVATEUSE, s);
}
public static boolean isExtensionSingletonChar(char c) {
return isExtensionSingleton(String.valueOf(c));
}
public static boolean isExtensionSubtag(String s) {
// extension = singleton 1*("-" (2*8alphanum))
int len = s.length();
return (len >= 2) && (len <= 8) && LocaleUtils.isAlphaNumericString(s);
}
public static boolean isPrivateusePrefix(String s) {
// privateuse = "x" 1*("-" (1*8alphanum))
return (s.length() == 1)
&& LocaleUtils.caseIgnoreMatch(PRIVATEUSE, s);
}
public static boolean isPrivateusePrefixChar(char c) {
return (LocaleUtils.caseIgnoreMatch(PRIVATEUSE, String.valueOf(c)));
}
public static boolean isPrivateuseSubtag(String s) {
// privateuse = "x" 1*("-" (1*8alphanum))
int len = s.length();
return (len >= 1) && (len <= 8) && LocaleUtils.isAlphaNumericString(s);
}
//
// Language subtag canonicalization methods
//
public static String canonicalizeLanguage(String s) {
return LocaleUtils.toLowerString(s);
}
public static String canonicalizeExtlang(String s) {
return LocaleUtils.toLowerString(s);
}
public static String canonicalizeScript(String s) {
return LocaleUtils.toTitleString(s);
}
public static String canonicalizeRegion(String s) {
return LocaleUtils.toUpperString(s);
}
public static String canonicalizeVariant(String s) {
return LocaleUtils.toLowerString(s);
}
public static String canonicalizeExtension(String s) {
return LocaleUtils.toLowerString(s);
}
public static String canonicalizeExtensionSingleton(String s) {
return LocaleUtils.toLowerString(s);
}
public static String canonicalizeExtensionSubtag(String s) {
return LocaleUtils.toLowerString(s);
}
public static String canonicalizePrivateuse(String s) {
return LocaleUtils.toLowerString(s);
}
public static String canonicalizePrivateuseSubtag(String s) {
return LocaleUtils.toLowerString(s);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (language.length() > 0) {
sb.append(language);
for (String extlang : extlangs) {
sb.append(SEP).append(extlang);
}
if (script.length() > 0) {
sb.append(SEP).append(script);
}
if (region.length() > 0) {
sb.append(SEP).append(region);
}
for (String variant : variants) {
sb.append(SEP).append(variant);
}
for (String extension : extensions) {
sb.append(SEP).append(extension);
}
}
if (privateuse.length() > 0) {
if (sb.length() > 0) {
sb.append(SEP);
}
sb.append(privateuse);
}
return sb.toString();
}
}

View File

@@ -0,0 +1,305 @@
/*
* Copyright (c) 2012, 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.
*/
package sun.util.locale;
import java.util.HashMap;
import java.util.Map;
final class LocaleEquivalentMaps {
static final Map<String, String> singleEquivMap;
static final Map<String, String[]> multiEquivsMap;
static final Map<String, String> regionVariantEquivMap;
static {
singleEquivMap = new HashMap<>();
multiEquivsMap = new HashMap<>();
regionVariantEquivMap = new HashMap<>();
// This is an auto-generated file and should not be manually edited.
// LSR Revision: 2019-09-16
singleEquivMap.put("aam", "aas");
singleEquivMap.put("aas", "aam");
singleEquivMap.put("acn", "xia");
singleEquivMap.put("adp", "dz");
singleEquivMap.put("adx", "pcr");
singleEquivMap.put("ami", "i-ami");
singleEquivMap.put("aog", "myd");
singleEquivMap.put("art-lojban", "jbo");
singleEquivMap.put("asd", "snz");
singleEquivMap.put("ase", "sgn-us");
singleEquivMap.put("aue", "ktz");
singleEquivMap.put("ayx", "nun");
singleEquivMap.put("bcg", "bgm");
singleEquivMap.put("bfi", "sgn-gb");
singleEquivMap.put("bfy", "ppa");
singleEquivMap.put("bgm", "bcg");
singleEquivMap.put("bjd", "drl");
singleEquivMap.put("bmf", "krm");
singleEquivMap.put("bnn", "i-bnn");
singleEquivMap.put("bzs", "sgn-br");
singleEquivMap.put("cax", "xba");
singleEquivMap.put("cir", "meg");
singleEquivMap.put("cjr", "mom");
singleEquivMap.put("cka", "cmr");
singleEquivMap.put("cmk", "xch");
singleEquivMap.put("cmn-hans", "zh-cmn-hans");
singleEquivMap.put("cmn-hant", "zh-cmn-hant");
singleEquivMap.put("cmr", "cka");
singleEquivMap.put("cqu", "quh");
singleEquivMap.put("csn", "sgn-co");
singleEquivMap.put("dev", "gav");
singleEquivMap.put("dif", "dit");
singleEquivMap.put("dit", "dif");
singleEquivMap.put("drh", "khk");
singleEquivMap.put("drl", "bjd");
singleEquivMap.put("dse", "sgn-nl");
singleEquivMap.put("dsl", "sgn-dk");
singleEquivMap.put("duz", "guv");
singleEquivMap.put("dz", "adp");
singleEquivMap.put("ema", "uok");
singleEquivMap.put("en-gb-oed", "en-gb-oxendict");
singleEquivMap.put("en-gb-oxendict", "en-gb-oed");
singleEquivMap.put("fsl", "sgn-fr");
singleEquivMap.put("gal", "ilw");
singleEquivMap.put("gan", "zh-gan");
singleEquivMap.put("gav", "dev");
singleEquivMap.put("gdj", "kvs");
singleEquivMap.put("ggn", "gvr");
singleEquivMap.put("gsg", "sgn-de");
singleEquivMap.put("gss", "sgn-gr");
singleEquivMap.put("gti", "nyc");
singleEquivMap.put("guv", "duz");
singleEquivMap.put("gvr", "ggn");
singleEquivMap.put("he", "iw");
singleEquivMap.put("hle", "sca");
singleEquivMap.put("hrr", "jal");
singleEquivMap.put("hsn", "zh-xiang");
singleEquivMap.put("huw", "pmc");
singleEquivMap.put("i-ami", "ami");
singleEquivMap.put("i-bnn", "bnn");
singleEquivMap.put("i-klingon", "tlh");
singleEquivMap.put("i-lux", "lb");
singleEquivMap.put("i-navajo", "nv");
singleEquivMap.put("i-pwn", "pwn");
singleEquivMap.put("i-tao", "tao");
singleEquivMap.put("i-tay", "tay");
singleEquivMap.put("i-tsu", "tsu");
singleEquivMap.put("ibi", "opa");
singleEquivMap.put("id", "in");
singleEquivMap.put("ilw", "gal");
singleEquivMap.put("in", "id");
singleEquivMap.put("ise", "sgn-it");
singleEquivMap.put("isg", "sgn-ie");
singleEquivMap.put("iw", "he");
singleEquivMap.put("jal", "hrr");
singleEquivMap.put("jbo", "art-lojban");
singleEquivMap.put("ji", "yi");
singleEquivMap.put("jsl", "sgn-jp");
singleEquivMap.put("jv", "jw");
singleEquivMap.put("jw", "jv");
singleEquivMap.put("kak", "tne");
singleEquivMap.put("kdz", "ncp");
singleEquivMap.put("kgc", "tdf");
singleEquivMap.put("kgh", "kml");
singleEquivMap.put("khk", "drh");
singleEquivMap.put("kml", "kgh");
singleEquivMap.put("koj", "kwv");
singleEquivMap.put("krm", "bmf");
singleEquivMap.put("ktz", "aue");
singleEquivMap.put("kvs", "gdj");
singleEquivMap.put("kwq", "yam");
singleEquivMap.put("kwv", "koj");
singleEquivMap.put("kxe", "tvd");
singleEquivMap.put("lb", "i-lux");
singleEquivMap.put("lcq", "ppr");
singleEquivMap.put("lii", "raq");
singleEquivMap.put("llo", "ngt");
singleEquivMap.put("lmm", "rmx");
singleEquivMap.put("lrr", "yma");
singleEquivMap.put("meg", "cir");
singleEquivMap.put("mfs", "sgn-mx");
singleEquivMap.put("mo", "ro");
singleEquivMap.put("mom", "cjr");
singleEquivMap.put("mtm", "ymt");
singleEquivMap.put("myd", "aog");
singleEquivMap.put("nad", "xny");
singleEquivMap.put("nan", "zh-min-nan");
singleEquivMap.put("nb", "no-bok");
singleEquivMap.put("nbr", "nns");
singleEquivMap.put("ncp", "kdz");
singleEquivMap.put("ncs", "sgn-ni");
singleEquivMap.put("ngt", "llo");
singleEquivMap.put("ngv", "nnx");
singleEquivMap.put("nn", "no-nyn");
singleEquivMap.put("nns", "nbr");
singleEquivMap.put("nnx", "ngv");
singleEquivMap.put("no-bok", "nb");
singleEquivMap.put("no-nyn", "nn");
singleEquivMap.put("nsl", "sgn-no");
singleEquivMap.put("nun", "ayx");
singleEquivMap.put("nv", "i-navajo");
singleEquivMap.put("nyc", "gti");
singleEquivMap.put("opa", "ibi");
singleEquivMap.put("pcr", "adx");
singleEquivMap.put("phr", "pmu");
singleEquivMap.put("pmc", "huw");
singleEquivMap.put("pmu", "phr");
singleEquivMap.put("ppa", "bfy");
singleEquivMap.put("ppr", "lcq");
singleEquivMap.put("prt", "pry");
singleEquivMap.put("pry", "prt");
singleEquivMap.put("psr", "sgn-pt");
singleEquivMap.put("pub", "puz");
singleEquivMap.put("puz", "pub");
singleEquivMap.put("pwn", "i-pwn");
singleEquivMap.put("quh", "cqu");
singleEquivMap.put("raq", "lii");
singleEquivMap.put("ras", "tie");
singleEquivMap.put("rmx", "lmm");
singleEquivMap.put("ro", "mo");
singleEquivMap.put("sca", "hle");
singleEquivMap.put("sfb", "sgn-be-fr");
singleEquivMap.put("sfs", "sgn-za");
singleEquivMap.put("sgg", "sgn-ch-de");
singleEquivMap.put("sgn-be-fr", "sfb");
singleEquivMap.put("sgn-be-nl", "vgt");
singleEquivMap.put("sgn-br", "bzs");
singleEquivMap.put("sgn-ch-de", "sgg");
singleEquivMap.put("sgn-co", "csn");
singleEquivMap.put("sgn-de", "gsg");
singleEquivMap.put("sgn-dk", "dsl");
singleEquivMap.put("sgn-es", "ssp");
singleEquivMap.put("sgn-fr", "fsl");
singleEquivMap.put("sgn-gb", "bfi");
singleEquivMap.put("sgn-gr", "gss");
singleEquivMap.put("sgn-ie", "isg");
singleEquivMap.put("sgn-it", "ise");
singleEquivMap.put("sgn-jp", "jsl");
singleEquivMap.put("sgn-mx", "mfs");
singleEquivMap.put("sgn-ni", "ncs");
singleEquivMap.put("sgn-nl", "dse");
singleEquivMap.put("sgn-no", "nsl");
singleEquivMap.put("sgn-pt", "psr");
singleEquivMap.put("sgn-se", "swl");
singleEquivMap.put("sgn-us", "ase");
singleEquivMap.put("sgn-za", "sfs");
singleEquivMap.put("snz", "asd");
singleEquivMap.put("ssp", "sgn-es");
singleEquivMap.put("swl", "sgn-se");
singleEquivMap.put("taj", "tsf");
singleEquivMap.put("tao", "i-tao");
singleEquivMap.put("tay", "i-tay");
singleEquivMap.put("tdf", "kgc");
singleEquivMap.put("thc", "tpo");
singleEquivMap.put("tie", "ras");
singleEquivMap.put("tkk", "twm");
singleEquivMap.put("tlh", "i-klingon");
singleEquivMap.put("tlw", "weo");
singleEquivMap.put("tmp", "tyj");
singleEquivMap.put("tne", "kak");
singleEquivMap.put("tpo", "thc");
singleEquivMap.put("tsf", "taj");
singleEquivMap.put("tsu", "i-tsu");
singleEquivMap.put("tvd", "kxe");
singleEquivMap.put("twm", "tkk");
singleEquivMap.put("tyj", "tmp");
singleEquivMap.put("uok", "ema");
singleEquivMap.put("vgt", "sgn-be-nl");
singleEquivMap.put("waw", "xkh");
singleEquivMap.put("weo", "tlw");
singleEquivMap.put("wuu", "zh-wuu");
singleEquivMap.put("xba", "cax");
singleEquivMap.put("xch", "cmk");
singleEquivMap.put("xia", "acn");
singleEquivMap.put("xkh", "waw");
singleEquivMap.put("xny", "nad");
singleEquivMap.put("yam", "kwq");
singleEquivMap.put("yi", "ji");
singleEquivMap.put("yma", "lrr");
singleEquivMap.put("ymt", "mtm");
singleEquivMap.put("yos", "zom");
singleEquivMap.put("yue", "zh-yue");
singleEquivMap.put("yug", "yuu");
singleEquivMap.put("yuu", "yug");
singleEquivMap.put("zh-cmn-hans", "cmn-hans");
singleEquivMap.put("zh-cmn-hant", "cmn-hant");
singleEquivMap.put("zh-gan", "gan");
singleEquivMap.put("zh-min-nan", "nan");
singleEquivMap.put("zh-wuu", "wuu");
singleEquivMap.put("zh-xiang", "hsn");
singleEquivMap.put("zh-yue", "yue");
singleEquivMap.put("zom", "yos");
multiEquivsMap.put("ccq", new String[] {"rki", "ybd"});
multiEquivsMap.put("cmn", new String[] {"zh-guoyu", "zh-cmn"});
multiEquivsMap.put("coy", new String[] {"pij", "nts"});
multiEquivsMap.put("drw", new String[] {"prs", "tnf"});
multiEquivsMap.put("dtp", new String[] {"ktr", "kzj", "kzt", "tdu"});
multiEquivsMap.put("gfx", new String[] {"vaj", "mwj", "oun"});
multiEquivsMap.put("hak", new String[] {"i-hak", "zh-hakka"});
multiEquivsMap.put("i-hak", new String[] {"hak", "zh-hakka"});
multiEquivsMap.put("jeg", new String[] {"oyb", "skk", "thx"});
multiEquivsMap.put("ktr", new String[] {"dtp", "kzj", "kzt", "tdu"});
multiEquivsMap.put("kzj", new String[] {"dtp", "ktr", "kzt", "tdu"});
multiEquivsMap.put("kzt", new String[] {"dtp", "ktr", "kzj", "tdu"});
multiEquivsMap.put("mry", new String[] {"mst", "myt"});
multiEquivsMap.put("mst", new String[] {"mry", "myt"});
multiEquivsMap.put("mwj", new String[] {"vaj", "gfx", "oun"});
multiEquivsMap.put("myt", new String[] {"mry", "mst"});
multiEquivsMap.put("nts", new String[] {"pij", "coy"});
multiEquivsMap.put("oun", new String[] {"vaj", "gfx", "mwj"});
multiEquivsMap.put("oyb", new String[] {"jeg", "skk", "thx"});
multiEquivsMap.put("pij", new String[] {"coy", "nts"});
multiEquivsMap.put("prs", new String[] {"drw", "tnf"});
multiEquivsMap.put("rki", new String[] {"ccq", "ybd"});
multiEquivsMap.put("skk", new String[] {"oyb", "jeg", "thx"});
multiEquivsMap.put("tdu", new String[] {"dtp", "ktr", "kzj", "kzt"});
multiEquivsMap.put("thx", new String[] {"oyb", "jeg", "skk"});
multiEquivsMap.put("tnf", new String[] {"prs", "drw"});
multiEquivsMap.put("vaj", new String[] {"gfx", "mwj", "oun"});
multiEquivsMap.put("ybd", new String[] {"rki", "ccq"});
multiEquivsMap.put("zh-cmn", new String[] {"cmn", "zh-guoyu"});
multiEquivsMap.put("zh-guoyu", new String[] {"cmn", "zh-cmn"});
multiEquivsMap.put("zh-hakka", new String[] {"hak", "i-hak"});
regionVariantEquivMap.put("-alalc97", "-heploc");
regionVariantEquivMap.put("-bu", "-mm");
regionVariantEquivMap.put("-cd", "-zr");
regionVariantEquivMap.put("-dd", "-de");
regionVariantEquivMap.put("-de", "-dd");
regionVariantEquivMap.put("-fr", "-fx");
regionVariantEquivMap.put("-fx", "-fr");
regionVariantEquivMap.put("-heploc", "-alalc97");
regionVariantEquivMap.put("-mm", "-bu");
regionVariantEquivMap.put("-tl", "-tp");
regionVariantEquivMap.put("-tp", "-tl");
regionVariantEquivMap.put("-yd", "-ye");
regionVariantEquivMap.put("-ye", "-yd");
regionVariantEquivMap.put("-zr", "-cd");
}
}

View File

@@ -0,0 +1,242 @@
/*
* Copyright (c) 2010, 2011, 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 java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import sun.util.locale.InternalLocaleBuilder.CaseInsensitiveChar;
import sun.util.locale.InternalLocaleBuilder.CaseInsensitiveString;
public class LocaleExtensions {
private final Map<Character, Extension> extensionMap;
private final String id;
public static final LocaleExtensions CALENDAR_JAPANESE
= new LocaleExtensions("u-ca-japanese",
UnicodeLocaleExtension.SINGLETON,
UnicodeLocaleExtension.CA_JAPANESE);
public static final LocaleExtensions NUMBER_THAI
= new LocaleExtensions("u-nu-thai",
UnicodeLocaleExtension.SINGLETON,
UnicodeLocaleExtension.NU_THAI);
private LocaleExtensions(String id, Character key, Extension value) {
this.id = id;
this.extensionMap = Collections.singletonMap(key, value);
}
/*
* Package private constructor, only used by InternalLocaleBuilder.
*/
LocaleExtensions(Map<CaseInsensitiveChar, String> extensions,
Set<CaseInsensitiveString> uattributes,
Map<CaseInsensitiveString, String> ukeywords) {
boolean hasExtension = !LocaleUtils.isEmpty(extensions);
boolean hasUAttributes = !LocaleUtils.isEmpty(uattributes);
boolean hasUKeywords = !LocaleUtils.isEmpty(ukeywords);
if (!hasExtension && !hasUAttributes && !hasUKeywords) {
id = "";
extensionMap = Collections.emptyMap();
return;
}
// Build extension map
SortedMap<Character, Extension> map = new TreeMap<>();
if (hasExtension) {
for (Entry<CaseInsensitiveChar, String> ext : extensions.entrySet()) {
char key = LocaleUtils.toLower(ext.getKey().value());
String value = ext.getValue();
if (LanguageTag.isPrivateusePrefixChar(key)) {
// we need to exclude special variant in privuateuse, e.g. "x-abc-lvariant-DEF"
value = InternalLocaleBuilder.removePrivateuseVariant(value);
if (value == null) {
continue;
}
}
map.put(key, new Extension(key, LocaleUtils.toLowerString(value)));
}
}
if (hasUAttributes || hasUKeywords) {
SortedSet<String> uaset = null;
SortedMap<String, String> ukmap = null;
if (hasUAttributes) {
uaset = new TreeSet<>();
for (CaseInsensitiveString cis : uattributes) {
uaset.add(LocaleUtils.toLowerString(cis.value()));
}
}
if (hasUKeywords) {
ukmap = new TreeMap<>();
for (Entry<CaseInsensitiveString, String> kwd : ukeywords.entrySet()) {
String key = LocaleUtils.toLowerString(kwd.getKey().value());
String type = LocaleUtils.toLowerString(kwd.getValue());
ukmap.put(key, type);
}
}
UnicodeLocaleExtension ule = new UnicodeLocaleExtension(uaset, ukmap);
map.put(UnicodeLocaleExtension.SINGLETON, ule);
}
if (map.isEmpty()) {
// this could happen when only privuateuse with special variant
id = "";
extensionMap = Collections.emptyMap();
} else {
id = toID(map);
extensionMap = map;
}
}
public Set<Character> getKeys() {
if (extensionMap.isEmpty()) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(extensionMap.keySet());
}
public Extension getExtension(Character key) {
return extensionMap.get(LocaleUtils.toLower(key));
}
public String getExtensionValue(Character key) {
Extension ext = extensionMap.get(LocaleUtils.toLower(key));
if (ext == null) {
return null;
}
return ext.getValue();
}
public Set<String> getUnicodeLocaleAttributes() {
Extension ext = extensionMap.get(UnicodeLocaleExtension.SINGLETON);
if (ext == null) {
return Collections.emptySet();
}
assert (ext instanceof UnicodeLocaleExtension);
return ((UnicodeLocaleExtension)ext).getUnicodeLocaleAttributes();
}
public Set<String> getUnicodeLocaleKeys() {
Extension ext = extensionMap.get(UnicodeLocaleExtension.SINGLETON);
if (ext == null) {
return Collections.emptySet();
}
assert (ext instanceof UnicodeLocaleExtension);
return ((UnicodeLocaleExtension)ext).getUnicodeLocaleKeys();
}
public String getUnicodeLocaleType(String unicodeLocaleKey) {
Extension ext = extensionMap.get(UnicodeLocaleExtension.SINGLETON);
if (ext == null) {
return null;
}
assert (ext instanceof UnicodeLocaleExtension);
return ((UnicodeLocaleExtension)ext).getUnicodeLocaleType(LocaleUtils.toLowerString(unicodeLocaleKey));
}
public boolean isEmpty() {
return extensionMap.isEmpty();
}
public static boolean isValidKey(char c) {
return LanguageTag.isExtensionSingletonChar(c) || LanguageTag.isPrivateusePrefixChar(c);
}
public static boolean isValidUnicodeLocaleKey(String ukey) {
return UnicodeLocaleExtension.isKey(ukey);
}
private static String toID(SortedMap<Character, Extension> map) {
StringBuilder buf = new StringBuilder();
Extension privuse = null;
for (Entry<Character, Extension> entry : map.entrySet()) {
char singleton = entry.getKey();
Extension extension = entry.getValue();
if (LanguageTag.isPrivateusePrefixChar(singleton)) {
privuse = extension;
} else {
if (buf.length() > 0) {
buf.append(LanguageTag.SEP);
}
buf.append(extension);
}
}
if (privuse != null) {
if (buf.length() > 0) {
buf.append(LanguageTag.SEP);
}
buf.append(privuse);
}
return buf.toString();
}
@Override
public String toString() {
return id;
}
public String getID() {
return id;
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof LocaleExtensions)) {
return false;
}
return id.equals(((LocaleExtensions)other).id);
}
}

View File

@@ -0,0 +1,455 @@
/*
* Copyright (c) 2012, 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.
*/
package sun.util.locale;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Locale.*;
import static java.util.Locale.FilteringMode.*;
import static java.util.Locale.LanguageRange.*;
import java.util.Map;
import java.util.Set;
/**
* Implementation for BCP47 Locale matching
*
*/
public final class LocaleMatcher {
public static List<Locale> filter(List<LanguageRange> priorityList,
Collection<Locale> locales,
FilteringMode mode) {
if (priorityList.isEmpty() || locales.isEmpty()) {
return new ArrayList<>(); // need to return a empty mutable List
}
// Create a list of language tags to be matched.
List<String> tags = new ArrayList<>();
for (Locale locale : locales) {
tags.add(locale.toLanguageTag());
}
// Filter language tags.
List<String> filteredTags = filterTags(priorityList, tags, mode);
// Create a list of matching locales.
List<Locale> filteredLocales = new ArrayList<>(filteredTags.size());
for (String tag : filteredTags) {
filteredLocales.add(Locale.forLanguageTag(tag));
}
return filteredLocales;
}
public static List<String> filterTags(List<LanguageRange> priorityList,
Collection<String> tags,
FilteringMode mode) {
if (priorityList.isEmpty() || tags.isEmpty()) {
return new ArrayList<>(); // need to return a empty mutable List
}
ArrayList<LanguageRange> list;
if (mode == EXTENDED_FILTERING) {
return filterExtended(priorityList, tags);
} else {
list = new ArrayList<>();
for (LanguageRange lr : priorityList) {
String range = lr.getRange();
if (range.startsWith("*-")
|| range.indexOf("-*") != -1) { // Extended range
if (mode == AUTOSELECT_FILTERING) {
return filterExtended(priorityList, tags);
} else if (mode == MAP_EXTENDED_RANGES) {
if (range.charAt(0) == '*') {
range = "*";
} else {
range = range.replaceAll("-[*]", "");
}
list.add(new LanguageRange(range, lr.getWeight()));
} else if (mode == REJECT_EXTENDED_RANGES) {
throw new IllegalArgumentException("An extended range \""
+ range
+ "\" found in REJECT_EXTENDED_RANGES mode.");
}
} else { // Basic range
list.add(lr);
}
}
return filterBasic(list, tags);
}
}
private static List<String> filterBasic(List<LanguageRange> priorityList,
Collection<String> tags) {
List<String> list = new ArrayList<>();
for (LanguageRange lr : priorityList) {
String range = lr.getRange();
if (range.equals("*")) {
return new ArrayList<String>(tags);
} else {
for (String tag : tags) {
tag = tag.toLowerCase();
if (tag.startsWith(range)) {
int len = range.length();
if ((tag.length() == len || tag.charAt(len) == '-')
&& !list.contains(tag)) {
list.add(tag);
}
}
}
}
}
return list;
}
private static List<String> filterExtended(List<LanguageRange> priorityList,
Collection<String> tags) {
List<String> list = new ArrayList<>();
for (LanguageRange lr : priorityList) {
String range = lr.getRange();
if (range.equals("*")) {
return new ArrayList<String>(tags);
}
String[] rangeSubtags = range.split("-");
for (String tag : tags) {
tag = tag.toLowerCase();
String[] tagSubtags = tag.split("-");
if (!rangeSubtags[0].equals(tagSubtags[0])
&& !rangeSubtags[0].equals("*")) {
continue;
}
int rangeIndex = 1;
int tagIndex = 1;
while (rangeIndex < rangeSubtags.length
&& tagIndex < tagSubtags.length) {
if (rangeSubtags[rangeIndex].equals("*")) {
rangeIndex++;
} else if (rangeSubtags[rangeIndex].equals(tagSubtags[tagIndex])) {
rangeIndex++;
tagIndex++;
} else if (tagSubtags[tagIndex].length() == 1
&& !tagSubtags[tagIndex].equals("*")) {
break;
} else {
tagIndex++;
}
}
if (rangeSubtags.length == rangeIndex && !list.contains(tag)) {
list.add(tag);
}
}
}
return list;
}
public static Locale lookup(List<LanguageRange> priorityList,
Collection<Locale> locales) {
if (priorityList.isEmpty() || locales.isEmpty()) {
return null;
}
// Create a list of language tags to be matched.
List<String> tags = new ArrayList<>();
for (Locale locale : locales) {
tags.add(locale.toLanguageTag());
}
// Look up a language tags.
String lookedUpTag = lookupTag(priorityList, tags);
if (lookedUpTag == null) {
return null;
} else {
return Locale.forLanguageTag(lookedUpTag);
}
}
public static String lookupTag(List<LanguageRange> priorityList,
Collection<String> tags) {
if (priorityList.isEmpty() || tags.isEmpty()) {
return null;
}
for (LanguageRange lr : priorityList) {
String range = lr.getRange();
// Special language range ("*") is ignored in lookup.
if (range.equals("*")) {
continue;
}
String rangeForRegex = range.replaceAll("\\x2A", "\\\\p{Alnum}*");
while (rangeForRegex.length() > 0) {
for (String tag : tags) {
tag = tag.toLowerCase();
if (tag.matches(rangeForRegex)) {
return tag;
}
}
// Truncate from the end....
int index = rangeForRegex.lastIndexOf('-');
if (index >= 0) {
rangeForRegex = rangeForRegex.substring(0, index);
// if range ends with an extension key, truncate it.
if (rangeForRegex.lastIndexOf('-') == rangeForRegex.length()-2) {
rangeForRegex =
rangeForRegex.substring(0, rangeForRegex.length()-2);
}
} else {
rangeForRegex = "";
}
}
}
return null;
}
public static List<LanguageRange> parse(String ranges) {
ranges = ranges.replaceAll(" ", "").toLowerCase();
if (ranges.startsWith("accept-language:")) {
ranges = ranges.substring(16); // delete unnecessary prefix
}
String[] langRanges = ranges.split(",");
List<LanguageRange> list = new ArrayList<>(langRanges.length);
List<String> tempList = new ArrayList<>();
int numOfRanges = 0;
for (String range : langRanges) {
int index;
String r;
double w;
if ((index = range.indexOf(";q=")) == -1) {
r = range;
w = MAX_WEIGHT;
} else {
r = range.substring(0, index);
index += 3;
try {
w = Double.parseDouble(range.substring(index));
}
catch (Exception e) {
throw new IllegalArgumentException("weight=\""
+ range.substring(index)
+ "\" for language range \"" + r + "\"");
}
if (w < MIN_WEIGHT || w > MAX_WEIGHT) {
throw new IllegalArgumentException("weight=" + w
+ " for language range \"" + r
+ "\". It must be between " + MIN_WEIGHT
+ " and " + MAX_WEIGHT + ".");
}
}
if (!tempList.contains(r)) {
LanguageRange lr = new LanguageRange(r, w);
index = numOfRanges;
for (int j = 0; j < numOfRanges; j++) {
if (list.get(j).getWeight() < w) {
index = j;
break;
}
}
list.add(index, lr);
numOfRanges++;
tempList.add(r);
// Check if the range has an equivalent using IANA LSR data.
// If yes, add it to the User's Language Priority List as well.
// aa-XX -> aa-YY
String equivalent;
if ((equivalent = getEquivalentForRegionAndVariant(r)) != null
&& !tempList.contains(equivalent)) {
list.add(index+1, new LanguageRange(equivalent, w));
numOfRanges++;
tempList.add(equivalent);
}
String[] equivalents;
if ((equivalents = getEquivalentsForLanguage(r)) != null) {
for (String equiv: equivalents) {
// aa-XX -> bb-XX(, cc-XX)
if (!tempList.contains(equiv)) {
list.add(index+1, new LanguageRange(equiv, w));
numOfRanges++;
tempList.add(equiv);
}
// bb-XX -> bb-YY(, cc-YY)
equivalent = getEquivalentForRegionAndVariant(equiv);
if (equivalent != null
&& !tempList.contains(equivalent)) {
list.add(index+1, new LanguageRange(equivalent, w));
numOfRanges++;
tempList.add(equivalent);
}
}
}
}
}
return list;
}
private static String[] getEquivalentsForLanguage(String range) {
String r = range;
while (r.length() > 0) {
if (LocaleEquivalentMaps.singleEquivMap.containsKey(r)) {
String equiv = LocaleEquivalentMaps.singleEquivMap.get(r);
// Return immediately for performance if the first matching
// subtag is found.
return new String[] {range.replaceFirst(r, equiv)};
} else if (LocaleEquivalentMaps.multiEquivsMap.containsKey(r)) {
String[] equivs = LocaleEquivalentMaps.multiEquivsMap.get(r);
for (int i = 0; i < equivs.length; i++) {
equivs[i] = range.replaceFirst(r, equivs[i]);
}
return equivs;
}
// Truncate the last subtag simply.
int index = r.lastIndexOf('-');
if (index == -1) {
break;
}
r = r.substring(0, index);
}
return null;
}
private static String getEquivalentForRegionAndVariant(String range) {
int extensionKeyIndex = getExtentionKeyIndex(range);
for (String subtag : LocaleEquivalentMaps.regionVariantEquivMap.keySet()) {
int index;
if ((index = range.indexOf(subtag)) != -1) {
// Check if the matching text is a valid region or variant.
if (extensionKeyIndex != Integer.MIN_VALUE
&& index > extensionKeyIndex) {
continue;
}
int len = index + subtag.length();
if (range.length() == len || range.charAt(len) == '-') {
return range.replaceFirst(subtag, LocaleEquivalentMaps.regionVariantEquivMap.get(subtag));
}
}
}
return null;
}
private static int getExtentionKeyIndex(String s) {
char[] c = s.toCharArray();
int index = Integer.MIN_VALUE;
for (int i = 1; i < c.length; i++) {
if (c[i] == '-') {
if (i - index == 2) {
return index;
} else {
index = i;
}
}
}
return Integer.MIN_VALUE;
}
public static List<LanguageRange> mapEquivalents(
List<LanguageRange>priorityList,
Map<String, List<String>> map) {
if (priorityList.isEmpty()) {
return new ArrayList<>(); // need to return a empty mutable List
}
if (map == null || map.isEmpty()) {
return new ArrayList<LanguageRange>(priorityList);
}
// Create a map, key=originalKey.toLowerCaes(), value=originalKey
Map<String, String> keyMap = new HashMap<>();
for (String key : map.keySet()) {
keyMap.put(key.toLowerCase(), key);
}
List<LanguageRange> list = new ArrayList<>();
for (LanguageRange lr : priorityList) {
String range = lr.getRange();
String r = range;
boolean hasEquivalent = false;
while (r.length() > 0) {
if (keyMap.containsKey(r)) {
hasEquivalent = true;
List<String> equivalents = map.get(keyMap.get(r));
if (equivalents != null) {
int len = r.length();
for (String equivalent : equivalents) {
list.add(new LanguageRange(equivalent.toLowerCase()
+ range.substring(len),
lr.getWeight()));
}
}
// Return immediately if the first matching subtag is found.
break;
}
// Truncate the last subtag simply.
int index = r.lastIndexOf('-');
if (index == -1) {
break;
}
r = r.substring(0, index);
}
if (!hasEquivalent) {
list.add(lr);
}
}
return list;
}
private LocaleMatcher() {}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) 2010, 2011, 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 java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public abstract class LocaleObjectCache<K, V> {
private ConcurrentMap<K, CacheEntry<K, V>> map;
private ReferenceQueue<V> queue = new ReferenceQueue<>();
public LocaleObjectCache() {
this(16, 0.75f, 16);
}
public LocaleObjectCache(int initialCapacity, float loadFactor, int concurrencyLevel) {
map = new ConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel);
}
public V get(K key) {
V value = null;
cleanStaleEntries();
CacheEntry<K, V> entry = map.get(key);
if (entry != null) {
value = entry.get();
}
if (value == null) {
V newVal = createObject(key);
// make sure key is normalized *after* the object creation
// so that newVal is assured to be created from a valid key.
key = normalizeKey(key);
if (key == null || newVal == null) {
// subclass must return non-null key/value object
return null;
}
CacheEntry<K, V> newEntry = new CacheEntry<>(key, newVal, queue);
entry = map.putIfAbsent(key, newEntry);
if (entry == null) {
value = newVal;
} else {
value = entry.get();
if (value == null) {
map.put(key, newEntry);
value = newVal;
}
}
}
return value;
}
protected V put(K key, V value) {
CacheEntry<K, V> entry = new CacheEntry<>(key, value, queue);
CacheEntry<K, V> oldEntry = map.put(key, entry);
return (oldEntry == null) ? null : oldEntry.get();
}
@SuppressWarnings("unchecked")
private void cleanStaleEntries() {
CacheEntry<K, V> entry;
while ((entry = (CacheEntry<K, V>)queue.poll()) != null) {
map.remove(entry.getKey());
}
}
protected abstract V createObject(K key);
protected K normalizeKey(K key) {
return key;
}
private static class CacheEntry<K, V> extends SoftReference<V> {
private K key;
CacheEntry(K key, V value, ReferenceQueue<V> queue) {
super(value, queue);
this.key = key;
}
K getKey() {
return key;
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2010, 2011, 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, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
public class LocaleSyntaxException extends Exception {
private static final long serialVersionUID = 1L;
private int index = -1;
public LocaleSyntaxException(String msg) {
this(msg, 0);
}
public LocaleSyntaxException(String msg, int errorIndex) {
super(msg);
index = errorIndex;
}
public int getErrorIndex() {
return index;
}
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright (c) 2010, 2012, 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, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Collection of static utility methods for Locale support. The
* methods which manipulate characters or strings support ASCII only.
*/
public final class LocaleUtils {
private LocaleUtils() {
}
/**
* Compares two ASCII Strings s1 and s2, ignoring case.
*/
public static boolean caseIgnoreMatch(String s1, String s2) {
if (s1 == s2) {
return true;
}
int len = s1.length();
if (len != s2.length()) {
return false;
}
for (int i = 0; i < len; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2 && toLower(c1) != toLower(c2)) {
return false;
}
}
return true;
}
static int caseIgnoreCompare(String s1, String s2) {
if (s1 == s2) {
return 0;
}
return toLowerString(s1).compareTo(toLowerString(s2));
}
static char toUpper(char c) {
return isLower(c) ? (char)(c - 0x20) : c;
}
static char toLower(char c) {
return isUpper(c) ? (char)(c + 0x20) : c;
}
/**
* Converts the given ASCII String to lower-case.
*/
public static String toLowerString(String s) {
int len = s.length();
int idx = 0;
for (; idx < len; idx++) {
if (isUpper(s.charAt(idx))) {
break;
}
}
if (idx == len) {
return s;
}
char[] buf = new char[len];
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
buf[i] = (i < idx) ? c : toLower(c);
}
return new String(buf);
}
static String toUpperString(String s) {
int len = s.length();
int idx = 0;
for (; idx < len; idx++) {
if (isLower(s.charAt(idx))) {
break;
}
}
if (idx == len) {
return s;
}
char[] buf = new char[len];
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
buf[i] = (i < idx) ? c : toUpper(c);
}
return new String(buf);
}
static String toTitleString(String s) {
int len;
if ((len = s.length()) == 0) {
return s;
}
int idx = 0;
if (!isLower(s.charAt(idx))) {
for (idx = 1; idx < len; idx++) {
if (isUpper(s.charAt(idx))) {
break;
}
}
}
if (idx == len) {
return s;
}
char[] buf = new char[len];
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (i == 0 && idx == 0) {
buf[i] = toUpper(c);
} else if (i < idx) {
buf[i] = c;
} else {
buf[i] = toLower(c);
}
}
return new String(buf);
}
private static boolean isUpper(char c) {
return c >= 'A' && c <= 'Z';
}
private static boolean isLower(char c) {
return c >= 'a' && c <= 'z';
}
static boolean isAlpha(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
static boolean isAlphaString(String s) {
int len = s.length();
for (int i = 0; i < len; i++) {
if (!isAlpha(s.charAt(i))) {
return false;
}
}
return true;
}
static boolean isNumeric(char c) {
return (c >= '0' && c <= '9');
}
static boolean isNumericString(String s) {
int len = s.length();
for (int i = 0; i < len; i++) {
if (!isNumeric(s.charAt(i))) {
return false;
}
}
return true;
}
static boolean isAlphaNumeric(char c) {
return isAlpha(c) || isNumeric(c);
}
public static boolean isAlphaNumericString(String s) {
int len = s.length();
for (int i = 0; i < len; i++) {
if (!isAlphaNumeric(s.charAt(i))) {
return false;
}
}
return true;
}
static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
static boolean isEmpty(Set<?> set) {
return set == null || set.isEmpty();
}
static boolean isEmpty(Map<?, ?> map) {
return map == null || map.isEmpty();
}
static boolean isEmpty(List<?> list) {
return list == null || list.isEmpty();
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2010, 2011, 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) 2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
public class ParseStatus {
int parseLength;
int errorIndex;
String errorMsg;
public ParseStatus() {
reset();
}
public void reset() {
parseLength = 0;
errorIndex = -1;
errorMsg = null;
}
public boolean isError() {
return (errorIndex >= 0);
}
public int getErrorIndex() {
return errorIndex;
}
public int getParseLength() {
return parseLength;
}
public String getErrorMessage() {
return errorMsg;
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2010, 2011, 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, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package sun.util.locale;
public class StringTokenIterator {
private String text;
private String dlms; // null if a single char delimiter
private char delimiterChar; // delimiter if a single char delimiter
private String token;
private int start;
private int end;
private boolean done;
public StringTokenIterator(String text, String dlms) {
this.text = text;
if (dlms.length() == 1) {
delimiterChar = dlms.charAt(0);
} else {
this.dlms = dlms;
}
setStart(0);
}
public String first() {
setStart(0);
return token;
}
public String current() {
return token;
}
public int currentStart() {
return start;
}
public int currentEnd() {
return end;
}
public boolean isDone() {
return done;
}
public String next() {
if (hasNext()) {
start = end + 1;
end = nextDelimiter(start);
token = text.substring(start, end);
} else {
start = end;
token = null;
done = true;
}
return token;
}
public boolean hasNext() {
return (end < text.length());
}
public StringTokenIterator setStart(int offset) {
if (offset > text.length()) {
throw new IndexOutOfBoundsException();
}
start = offset;
end = nextDelimiter(start);
token = text.substring(start, end);
done = false;
return this;
}
public StringTokenIterator setText(String text) {
this.text = text;
setStart(0);
return this;
}
private int nextDelimiter(int start) {
int textlen = this.text.length();
if (dlms == null) {
for (int idx = start; idx < textlen; idx++) {
if (text.charAt(idx) == delimiterChar) {
return idx;
}
}
} else {
int dlmslen = dlms.length();
for (int idx = start; idx < textlen; idx++) {
char c = text.charAt(idx);
for (int i = 0; i < dlmslen; i++) {
if (c == dlms.charAt(i)) {
return idx;
}
}
}
}
return textlen;
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2010, 2011, 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 java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
public class UnicodeLocaleExtension extends Extension {
public static final char SINGLETON = 'u';
private final Set<String> attributes;
private final Map<String, String> keywords;
public static final UnicodeLocaleExtension CA_JAPANESE
= new UnicodeLocaleExtension("ca", "japanese");
public static final UnicodeLocaleExtension NU_THAI
= new UnicodeLocaleExtension("nu", "thai");
private UnicodeLocaleExtension(String key, String value) {
super(SINGLETON, key + "-" + value);
attributes = Collections.emptySet();
keywords = Collections.singletonMap(key, value);
}
UnicodeLocaleExtension(SortedSet<String> attributes, SortedMap<String, String> keywords) {
super(SINGLETON);
if (attributes != null) {
this.attributes = attributes;
} else {
this.attributes = Collections.emptySet();
}
if (keywords != null) {
this.keywords = keywords;
} else {
this.keywords = Collections.emptyMap();
}
if (!this.attributes.isEmpty() || !this.keywords.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (String attribute : this.attributes) {
sb.append(LanguageTag.SEP).append(attribute);
}
for (Entry<String, String> keyword : this.keywords.entrySet()) {
String key = keyword.getKey();
String value = keyword.getValue();
sb.append(LanguageTag.SEP).append(key);
if (value.length() > 0) {
sb.append(LanguageTag.SEP).append(value);
}
}
setValue(sb.substring(1)); // skip leading '-'
}
}
public Set<String> getUnicodeLocaleAttributes() {
if (attributes == Collections.EMPTY_SET) {
return attributes;
}
return Collections.unmodifiableSet(attributes);
}
public Set<String> getUnicodeLocaleKeys() {
if (keywords == Collections.EMPTY_MAP) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(keywords.keySet());
}
public String getUnicodeLocaleType(String unicodeLocaleKey) {
return keywords.get(unicodeLocaleKey);
}
public static boolean isSingletonChar(char c) {
return (SINGLETON == LocaleUtils.toLower(c));
}
public static boolean isAttribute(String s) {
// 3*8alphanum
int len = s.length();
return (len >= 3) && (len <= 8) && LocaleUtils.isAlphaNumericString(s);
}
public static boolean isKey(String s) {
// 2alphanum
return (s.length() == 2) && LocaleUtils.isAlphaNumericString(s);
}
public static boolean isTypeSubtag(String s) {
// 3*8alphanum
int len = s.length();
return (len >= 3) && (len <= 8) && LocaleUtils.isAlphaNumericString(s);
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
package sun.util.locale.provider;
import java.text.spi.BreakIteratorProvider;
import java.text.spi.CollatorProvider;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import java.util.spi.LocaleServiceProvider;
import java.util.spi.TimeZoneNameProvider;
import sun.util.spi.CalendarProvider;
/**
* An abstract parent class for the
* HostLocaleProviderAdapter/SPILocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public abstract class AuxLocaleProviderAdapter extends LocaleProviderAdapter {
/**
* SPI implementations map
*/
private ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProvider> providersMap =
new ConcurrentHashMap<>();
/**
* Getter method for Locale Service Providers
*/
@Override
public <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c) {
@SuppressWarnings("unchecked")
P lsp = (P) providersMap.get(c);
if (lsp == null) {
lsp = findInstalledProvider(c);
providersMap.putIfAbsent(c, lsp == null ? NULL_PROVIDER : lsp);
}
return lsp;
}
/**
* Real body to find an implementation for each SPI.
*
* @param <P>
* @param c
* @return
*/
protected abstract <P extends LocaleServiceProvider> P findInstalledProvider(final Class<P> c);
@Override
public BreakIteratorProvider getBreakIteratorProvider() {
return getLocaleServiceProvider(BreakIteratorProvider.class);
}
@Override
public CollatorProvider getCollatorProvider() {
return getLocaleServiceProvider(CollatorProvider.class);
}
@Override
public DateFormatProvider getDateFormatProvider() {
return getLocaleServiceProvider(DateFormatProvider.class);
}
@Override
public DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
return getLocaleServiceProvider(DateFormatSymbolsProvider.class);
}
@Override
public DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {
return getLocaleServiceProvider(DecimalFormatSymbolsProvider.class);
}
@Override
public NumberFormatProvider getNumberFormatProvider() {
return getLocaleServiceProvider(NumberFormatProvider.class);
}
/**
* Getter methods for java.util.spi.* providers
*/
@Override
public CurrencyNameProvider getCurrencyNameProvider() {
return getLocaleServiceProvider(CurrencyNameProvider.class);
}
@Override
public LocaleNameProvider getLocaleNameProvider() {
return getLocaleServiceProvider(LocaleNameProvider.class);
}
@Override
public TimeZoneNameProvider getTimeZoneNameProvider() {
return getLocaleServiceProvider(TimeZoneNameProvider.class);
}
@Override
public CalendarDataProvider getCalendarDataProvider() {
return getLocaleServiceProvider(CalendarDataProvider.class);
}
@Override
public CalendarNameProvider getCalendarNameProvider() {
return getLocaleServiceProvider(CalendarNameProvider.class);
}
/**
* Getter methods for sun.util.spi.* providers
*/
@Override
public CalendarProvider getCalendarProvider() {
return getLocaleServiceProvider(CalendarProvider.class);
}
@Override
public LocaleResources getLocaleResources(Locale locale) {
return null;
}
private static Locale[] availableLocales = null;
@Override
public Locale[] getAvailableLocales() {
if (availableLocales == null) {
Set<Locale> avail = new HashSet<>();
for (Class<? extends LocaleServiceProvider> c :
LocaleServiceProviderPool.spiClasses) {
LocaleServiceProvider lsp = getLocaleServiceProvider(c);
if (lsp != null) {
avail.addAll(Arrays.asList(lsp.getAvailableLocales()));
}
}
availableLocales = avail.toArray(new Locale[0]);
}
// assuming caller won't mutate the array.
return availableLocales;
}
/**
* A dummy locale service provider that indicates there is no
* provider available
*/
private static NullProvider NULL_PROVIDER = new NullProvider();
private static class NullProvider extends LocaleServiceProvider {
@Override
public Locale[] getAvailableLocales() {
return new Locale[0];
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2012, 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.
*/
package sun.util.locale.provider;
import java.util.Set;
/**
* An interface to return a set of available language tags supported by a
* LocaleServiceProvider.
*
* @author Masayoshi Okutsu
*/
public interface AvailableLanguageTags {
/**
* Returns a set of available language tags of a LocaleServiceProvider.
* Note that the returned set doesn't contain the language tag for
* {@code Locale.Root}.
*
* @return a Set of available language tags.
*/
public Set<String> getAvailableLanguageTags();
}

View File

@@ -0,0 +1,343 @@
/*
* Copyright (c) 1999, 2012, 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.
*/
/*
*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package sun.util.locale.provider;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.MissingResourceException;
import sun.text.CompactByteArray;
import sun.text.SupplementaryCharacterData;
/**
* This is the class that represents the list of known words used by
* DictionaryBasedBreakIterator. The conceptual data structure used
* here is a trie: there is a node hanging off the root node for every
* letter that can start a word. Each of these nodes has a node hanging
* off of it for every letter that can be the second letter of a word
* if this node is the first letter, and so on. The trie is represented
* as a two-dimensional array that can be treated as a table of state
* transitions. Indexes are used to compress this array, taking
* advantage of the fact that this array will always be very sparse.
*/
class BreakDictionary {
//=========================================================================
// data members
//=========================================================================
/**
* The version of the dictionary that was read in.
*/
private static int supportedVersion = 1;
/**
* Maps from characters to column numbers. The main use of this is to
* avoid making room in the array for empty columns.
*/
private CompactByteArray columnMap = null;
private SupplementaryCharacterData supplementaryCharColumnMap = null;
/**
* The number of actual columns in the table
*/
private int numCols;
/**
* Columns are organized into groups of 32. This says how many
* column groups. (We could calculate this, but we store the
* value to avoid having to repeatedly calculate it.)
*/
private int numColGroups;
/**
* The actual compressed state table. Each conceptual row represents
* a state, and the cells in it contain the row numbers of the states
* to transition to for each possible letter. 0 is used to indicate
* an illegal combination of letters (i.e., the error state). The
* table is compressed by eliminating all the unpopulated (i.e., zero)
* cells. Multiple conceptual rows can then be doubled up in a single
* physical row by sliding them up and possibly shifting them to one
* side or the other so the populated cells don't collide. Indexes
* are used to identify unpopulated cells and to locate populated cells.
*/
private short[] table = null;
/**
* This index maps logical row numbers to physical row numbers
*/
private short[] rowIndex = null;
/**
* A bitmap is used to tell which cells in the comceptual table are
* populated. This array contains all the unique bit combinations
* in that bitmap. If the table is more than 32 columns wide,
* successive entries in this array are used for a single row.
*/
private int[] rowIndexFlags = null;
/**
* This index maps from a logical row number into the bitmap table above.
* (This keeps us from storing duplicate bitmap combinations.) Since there
* are a lot of rows with only one populated cell, instead of wasting space
* in the bitmap table, we just store a negative number in this index for
* rows with one populated cell. The absolute value of that number is
* the column number of the populated cell.
*/
private short[] rowIndexFlagsIndex = null;
/**
* For each logical row, this index contains a constant that is added to
* the logical column number to get the physical column number
*/
private byte[] rowIndexShifts = null;
//=========================================================================
// deserialization
//=========================================================================
BreakDictionary(String dictionaryName)
throws IOException, MissingResourceException {
readDictionaryFile(dictionaryName);
}
private void readDictionaryFile(final String dictionaryName)
throws IOException, MissingResourceException {
BufferedInputStream in;
try {
in = AccessController.doPrivileged(
new PrivilegedExceptionAction<BufferedInputStream>() {
@Override
public BufferedInputStream run() throws Exception {
return new BufferedInputStream(getClass().getResourceAsStream("/sun/text/resources/" + dictionaryName));
}
}
);
}
catch (PrivilegedActionException e) {
throw new InternalError(e.toString(), e);
}
byte[] buf = new byte[8];
if (in.read(buf) != 8) {
throw new MissingResourceException("Wrong data length",
dictionaryName, "");
}
// check version
int version = RuleBasedBreakIterator.getInt(buf, 0);
if (version != supportedVersion) {
throw new MissingResourceException("Dictionary version(" + version + ") is unsupported",
dictionaryName, "");
}
// get data size
int len = RuleBasedBreakIterator.getInt(buf, 4);
buf = new byte[len];
if (in.read(buf) != len) {
throw new MissingResourceException("Wrong data length",
dictionaryName, "");
}
// close the stream
in.close();
int l;
int offset = 0;
// read in the column map for BMP characteres (this is serialized in
// its internal form: an index array followed by a data array)
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
short[] temp = new short[l];
for (int i = 0; i < l; i++, offset+=2) {
temp[i] = RuleBasedBreakIterator.getShort(buf, offset);
}
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
byte[] temp2 = new byte[l];
for (int i = 0; i < l; i++, offset++) {
temp2[i] = buf[offset];
}
columnMap = new CompactByteArray(temp, temp2);
// read in numCols and numColGroups
numCols = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
numColGroups = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
// read in the row-number index
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
rowIndex = new short[l];
for (int i = 0; i < l; i++, offset+=2) {
rowIndex[i] = RuleBasedBreakIterator.getShort(buf, offset);
}
// load in the populated-cells bitmap: index first, then bitmap list
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
rowIndexFlagsIndex = new short[l];
for (int i = 0; i < l; i++, offset+=2) {
rowIndexFlagsIndex[i] = RuleBasedBreakIterator.getShort(buf, offset);
}
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
rowIndexFlags = new int[l];
for (int i = 0; i < l; i++, offset+=4) {
rowIndexFlags[i] = RuleBasedBreakIterator.getInt(buf, offset);
}
// load in the row-shift index
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
rowIndexShifts = new byte[l];
for (int i = 0; i < l; i++, offset++) {
rowIndexShifts[i] = buf[offset];
}
// load in the actual state table
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
table = new short[l];
for (int i = 0; i < l; i++, offset+=2) {
table[i] = RuleBasedBreakIterator.getShort(buf, offset);
}
// finally, prepare the column map for supplementary characters
l = RuleBasedBreakIterator.getInt(buf, offset);
offset += 4;
int[] temp3 = new int[l];
for (int i = 0; i < l; i++, offset+=4) {
temp3[i] = RuleBasedBreakIterator.getInt(buf, offset);
}
supplementaryCharColumnMap = new SupplementaryCharacterData(temp3);
}
//=========================================================================
// access to the words
//=========================================================================
/**
* Uses the column map to map the character to a column number, then
* passes the row and column number to getNextState()
* @param row The current state
* @param ch The character whose column we're interested in
* @return The new state to transition to
*/
public final short getNextStateFromCharacter(int row, int ch) {
int col;
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
col = columnMap.elementAt((char)ch);
} else {
col = supplementaryCharColumnMap.getValue(ch);
}
return getNextState(row, col);
}
/**
* Returns the value in the cell with the specified (logical) row and
* column numbers. In DictionaryBasedBreakIterator, the row number is
* a state number, the column number is an input, and the return value
* is the row number of the new state to transition to. (0 is the
* "error" state, and -1 is the "end of word" state in a dictionary)
* @param row The row number of the current state
* @param col The column number of the input character (0 means "not a
* dictionary character")
* @return The row number of the new state to transition to
*/
public final short getNextState(int row, int col) {
if (cellIsPopulated(row, col)) {
// we map from logical to physical row number by looking up the
// mapping in rowIndex; we map from logical column number to
// physical column number by looking up a shift value for this
// logical row and offsetting the logical column number by
// the shift amount. Then we can use internalAt() to actually
// get the value out of the table.
return internalAt(rowIndex[row], col + rowIndexShifts[row]);
}
else {
return 0;
}
}
/**
* Given (logical) row and column numbers, returns true if the
* cell in that position is populated
*/
private boolean cellIsPopulated(int row, int col) {
// look up the entry in the bitmap index for the specified row.
// If it's a negative number, it's the column number of the only
// populated cell in the row
if (rowIndexFlagsIndex[row] < 0) {
return col == -rowIndexFlagsIndex[row];
}
// if it's a positive number, it's the offset of an entry in the bitmap
// list. If the table is more than 32 columns wide, the bitmap is stored
// successive entries in the bitmap list, so we have to divide the column
// number by 32 and offset the number we got out of the index by the result.
// Once we have the appropriate piece of the bitmap, test the appropriate
// bit and return the result.
else {
int flags = rowIndexFlags[rowIndexFlagsIndex[row] + (col >> 5)];
return (flags & (1 << (col & 0x1f))) != 0;
}
}
/**
* Implementation of getNextState() when we know the specified cell is
* populated.
* @param row The PHYSICAL row number of the cell
* @param col The PHYSICAL column number of the cell
* @return The value stored in the cell
*/
private short internalAt(int row, int col) {
// the table is a one-dimensional array, so this just does the math necessary
// to treat it as a two-dimensional array (we don't just use a two-dimensional
// array because two-dimensional arrays are inefficient in Java)
return table[row * numCols + col];
}
}

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 1999, 2013, 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.
*/
package sun.util.locale.provider;
import java.io.IOException;
import java.text.BreakIterator;
import java.text.spi.BreakIteratorProvider;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.BreakIteratorProvider
* BreakIteratorProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class BreakIteratorProviderImpl extends BreakIteratorProvider
implements AvailableLanguageTags {
private static final int CHARACTER_INDEX = 0;
private static final int WORD_INDEX = 1;
private static final int LINE_INDEX = 2;
private static final int SENTENCE_INDEX = 3;
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public BreakIteratorProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
/**
* Returns a new <code>BreakIterator</code> instance
* for <a href="../BreakIterator.html#word">word breaks</a>
* for the given locale.
* @param locale the desired locale
* @return A break iterator for word breaks
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.BreakIterator#getWordInstance(java.util.Locale)
*/
@Override
public BreakIterator getWordInstance(Locale locale) {
return getBreakInstance(locale,
WORD_INDEX,
"WordData",
"WordDictionary");
}
/**
* Returns a new <code>BreakIterator</code> instance
* for <a href="../BreakIterator.html#line">line breaks</a>
* for the given locale.
* @param locale the desired locale
* @return A break iterator for line breaks
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.BreakIterator#getLineInstance(java.util.Locale)
*/
@Override
public BreakIterator getLineInstance(Locale locale) {
return getBreakInstance(locale,
LINE_INDEX,
"LineData",
"LineDictionary");
}
/**
* Returns a new <code>BreakIterator</code> instance
* for <a href="../BreakIterator.html#character">character breaks</a>
* for the given locale.
* @param locale the desired locale
* @return A break iterator for character breaks
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.BreakIterator#getCharacterInstance(java.util.Locale)
*/
@Override
public BreakIterator getCharacterInstance(Locale locale) {
return getBreakInstance(locale,
CHARACTER_INDEX,
"CharacterData",
"CharacterDictionary");
}
/**
* Returns a new <code>BreakIterator</code> instance
* for <a href="../BreakIterator.html#sentence">sentence breaks</a>
* for the given locale.
* @param locale the desired locale
* @return A break iterator for sentence breaks
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.BreakIterator#getSentenceInstance(java.util.Locale)
*/
@Override
public BreakIterator getSentenceInstance(Locale locale) {
return getBreakInstance(locale,
SENTENCE_INDEX,
"SentenceData",
"SentenceDictionary");
}
private BreakIterator getBreakInstance(Locale locale,
int type,
String dataName,
String dictionaryName) {
if (locale == null) {
throw new NullPointerException();
}
LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(locale);
String[] classNames = (String[]) lr.getBreakIteratorInfo("BreakIteratorClasses");
String dataFile = (String) lr.getBreakIteratorInfo(dataName);
try {
switch (classNames[type]) {
case "RuleBasedBreakIterator":
return new RuleBasedBreakIterator(dataFile);
case "DictionaryBasedBreakIterator":
String dictionaryFile = (String) lr.getBreakIteratorInfo(dictionaryName);
return new DictionaryBasedBreakIterator(dataFile, dictionaryFile);
default:
throw new IllegalArgumentException("Invalid break iterator class \"" +
classNames[type] + "\"");
}
} catch (IOException | MissingResourceException | IllegalArgumentException e) {
throw new InternalError(e.toString(), e);
}
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
package sun.util.locale.provider;
import java.util.Locale;
import java.util.Set;
import java.util.spi.CalendarDataProvider;
/**
* Concrete implementation of the {@link java.util.spi.CalendarDataProvider
* CalendarDataProvider} class for the JRE LocaleProviderAdapter.
*
* @author Masayoshi Okutsu
* @author Naoto Sato
*/
public class CalendarDataProviderImpl extends CalendarDataProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CalendarDataProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
@Override
public int getFirstDayOfWeek(Locale locale) {
return LocaleProviderAdapter.forType(type).getLocaleResources(locale)
.getCalendarData(CalendarDataUtility.FIRST_DAY_OF_WEEK);
}
@Override
public int getMinimalDaysInFirstWeek(Locale locale) {
return LocaleProviderAdapter.forType(type).getLocaleResources(locale)
.getCalendarData(CalendarDataUtility.MINIMAL_DAYS_IN_FIRST_WEEK);
}
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
package sun.util.locale.provider;
import static java.util.Calendar.*;
import java.util.Locale;
import java.util.Map;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
/**
* {@code CalendarDataUtility} is a utility class for calling the
* {@link CalendarDataProvider} methods.
*
* @author Masayoshi Okutsu
* @author Naoto Sato
*/
public class CalendarDataUtility {
public final static String FIRST_DAY_OF_WEEK = "firstDayOfWeek";
public final static String MINIMAL_DAYS_IN_FIRST_WEEK = "minimalDaysInFirstWeek";
// No instantiation
private CalendarDataUtility() {
}
public static int retrieveFirstDayOfWeek(Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarDataProvider.class);
Integer value = pool.getLocalizedObject(CalendarWeekParameterGetter.INSTANCE,
locale, FIRST_DAY_OF_WEEK);
return (value != null && (value >= SUNDAY && value <= SATURDAY)) ? value : SUNDAY;
}
public static int retrieveMinimalDaysInFirstWeek(Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarDataProvider.class);
Integer value = pool.getLocalizedObject(CalendarWeekParameterGetter.INSTANCE,
locale, MINIMAL_DAYS_IN_FIRST_WEEK);
return (value != null && (value >= 1 && value <= 7)) ? value : 1;
}
public static String retrieveFieldValueName(String id, int field, int value, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
return pool.getLocalizedObject(CalendarFieldValueNameGetter.INSTANCE, locale, normalizeCalendarType(id),
field, value, style, false);
}
public static String retrieveJavaTimeFieldValueName(String id, int field, int value, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
String name;
name = pool.getLocalizedObject(CalendarFieldValueNameGetter.INSTANCE, locale, normalizeCalendarType(id),
field, value, style, true);
if (name == null) {
name = pool.getLocalizedObject(CalendarFieldValueNameGetter.INSTANCE, locale, normalizeCalendarType(id),
field, value, style, false);
}
return name;
}
public static Map<String, Integer> retrieveFieldValueNames(String id, int field, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
return pool.getLocalizedObject(CalendarFieldValueNamesMapGetter.INSTANCE, locale,
normalizeCalendarType(id), field, style, false);
}
public static Map<String, Integer> retrieveJavaTimeFieldValueNames(String id, int field, int style, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
Map<String, Integer> map;
map = pool.getLocalizedObject(CalendarFieldValueNamesMapGetter.INSTANCE, locale,
normalizeCalendarType(id), field, style, true);
if (map == null) {
map = pool.getLocalizedObject(CalendarFieldValueNamesMapGetter.INSTANCE, locale,
normalizeCalendarType(id), field, style, false);
}
return map;
}
static String normalizeCalendarType(String requestID) {
String type;
if (requestID.equals("gregorian") || requestID.equals("iso8601")) {
type = "gregory";
} else if (requestID.startsWith("islamic")) {
type = "islamic";
} else {
type = requestID;
}
return type;
}
/**
* Obtains a localized field value string from a CalendarDataProvider
* implementation.
*/
private static class CalendarFieldValueNameGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<CalendarNameProvider,
String> {
private static final CalendarFieldValueNameGetter INSTANCE =
new CalendarFieldValueNameGetter();
@Override
public String getObject(CalendarNameProvider calendarNameProvider,
Locale locale,
String requestID, // calendarType
Object... params) {
assert params.length == 4;
int field = (int) params[0];
int value = (int) params[1];
int style = (int) params[2];
boolean javatime = (boolean) params[3];
// If javatime is true, resources from CLDR have precedence over JRE
// native resources.
if (javatime && calendarNameProvider instanceof CalendarNameProviderImpl) {
String name;
name = ((CalendarNameProviderImpl)calendarNameProvider)
.getJavaTimeDisplayName(requestID, field, value, style, locale);
return name;
}
return calendarNameProvider.getDisplayName(requestID, field, value, style, locale);
}
}
/**
* Obtains a localized field-value pairs from a CalendarDataProvider
* implementation.
*/
private static class CalendarFieldValueNamesMapGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<CalendarNameProvider,
Map<String, Integer>> {
private static final CalendarFieldValueNamesMapGetter INSTANCE =
new CalendarFieldValueNamesMapGetter();
@Override
public Map<String, Integer> getObject(CalendarNameProvider calendarNameProvider,
Locale locale,
String requestID, // calendarType
Object... params) {
assert params.length == 3;
int field = (int) params[0];
int style = (int) params[1];
boolean javatime = (boolean) params[2];
// If javatime is true, resources from CLDR have precedence over JRE
// native resources.
if (javatime && calendarNameProvider instanceof CalendarNameProviderImpl) {
Map<String, Integer> map;
map = ((CalendarNameProviderImpl)calendarNameProvider)
.getJavaTimeDisplayNames(requestID, field, style, locale);
return map;
}
return calendarNameProvider.getDisplayNames(requestID, field, style, locale);
}
}
private static class CalendarWeekParameterGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<CalendarDataProvider,
Integer> {
private static final CalendarWeekParameterGetter INSTANCE =
new CalendarWeekParameterGetter();
@Override
public Integer getObject(CalendarDataProvider calendarDataProvider,
Locale locale,
String requestID, // resource key
Object... params) {
assert params.length == 0;
int value;
switch (requestID) {
case FIRST_DAY_OF_WEEK:
value = calendarDataProvider.getFirstDayOfWeek(locale);
break;
case MINIMAL_DAYS_IN_FIRST_WEEK:
value = calendarDataProvider.getMinimalDaysInFirstWeek(locale);
break;
default:
throw new InternalError("invalid requestID: " + requestID);
}
return (value != 0) ? value : null;
}
}
}

View File

@@ -0,0 +1,352 @@
/*
* Copyright (c) 2012, 2019, 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.
*/
package sun.util.locale.provider;
import static java.util.Calendar.*;
import java.util.Comparator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.spi.CalendarNameProvider;
import sun.util.calendar.CalendarSystem;
import sun.util.calendar.Era;
/**
* Concrete implementation of the {@link java.util.spi.CalendarNameProvider
* CalendarNameProvider} class for the JRE LocaleProviderAdapter.
*
* @author Masayoshi Okutsu
* @author Naoto Sato
*/
public class CalendarNameProviderImpl extends CalendarNameProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CalendarNameProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
@Override
public String getDisplayName(String calendarType, int field, int value, int style, Locale locale) {
return getDisplayNameImpl(calendarType, field, value, style, locale, false);
}
public String getJavaTimeDisplayName(String calendarType, int field, int value, int style, Locale locale) {
return getDisplayNameImpl(calendarType, field, value, style, locale, true);
}
public String getDisplayNameImpl(String calendarType, int field, int value, int style, Locale locale, boolean javatime) {
String name = null;
String key = getResourceKey(calendarType, field, style, javatime);
if (key != null) {
LocaleResources lr = LocaleProviderAdapter.forType(type).getLocaleResources(locale);
String[] strings = javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key);
if (strings != null && strings.length > 0) {
if (field == DAY_OF_WEEK || field == YEAR) {
--value;
}
if (value < 0) {
return null;
} else if (value >= strings.length) {
if (field == ERA && "japanese".equals(calendarType)) {
Era[] jeras = CalendarSystem.forName("japanese").getEras();
if (value <= jeras.length) {
// Localized era name could not be retrieved from this provider.
// This can occur either for Reiwa or SupEra.
//
// If it's CLDR provider, try COMPAT first, which is guaranteed to have
// the name for Reiwa.
if (type == LocaleProviderAdapter.Type.CLDR) {
lr = LocaleProviderAdapter.forJRE().getLocaleResources(locale);
key = getResourceKeyFor(LocaleProviderAdapter.Type.JRE,
calendarType, field, style, javatime);
strings =
javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key);
}
if (strings == null || value >= strings.length) {
// Get the default name for SupEra
Era supEra = jeras[value - 1]; // 0-based index
if (javatime) {
return getBaseStyle(style) == NARROW_FORMAT ?
supEra.getAbbreviation() :
supEra.getName();
} else {
return (style & LONG) != 0 ?
supEra.getName() :
supEra.getAbbreviation();
}
}
} else {
return null;
}
} else {
return null;
}
}
name = strings[value];
// If name is empty in standalone, try its `format' style.
if (name.length() == 0
&& (style == SHORT_STANDALONE || style == LONG_STANDALONE
|| style == NARROW_STANDALONE)) {
name = getDisplayName(calendarType, field, value,
getBaseStyle(style),
locale);
}
}
}
return name;
}
private static int[] REST_OF_STYLES = {
SHORT_STANDALONE, LONG_FORMAT, LONG_STANDALONE,
NARROW_FORMAT, NARROW_STANDALONE
};
@Override
public Map<String, Integer> getDisplayNames(String calendarType, int field, int style, Locale locale) {
Map<String, Integer> names;
if (style == ALL_STYLES) {
names = getDisplayNamesImpl(calendarType, field, SHORT_FORMAT, locale, false);
for (int st : REST_OF_STYLES) {
names.putAll(getDisplayNamesImpl(calendarType, field, st, locale, false));
}
} else {
// specific style
names = getDisplayNamesImpl(calendarType, field, style, locale, false);
}
return names.isEmpty() ? null : names;
}
// NOTE: This method should be used ONLY BY JSR 310 classes.
public Map<String, Integer> getJavaTimeDisplayNames(String calendarType, int field, int style, Locale locale) {
Map<String, Integer> names;
names = getDisplayNamesImpl(calendarType, field, style, locale, true);
return names.isEmpty() ? null : names;
}
private Map<String, Integer> getDisplayNamesImpl(String calendarType, int field,
int style, Locale locale, boolean javatime) {
String key = getResourceKey(calendarType, field, style, javatime);
Map<String, Integer> map = new TreeMap<>(LengthBasedComparator.INSTANCE);
if (key != null) {
LocaleResources lr = LocaleProviderAdapter.forType(type).getLocaleResources(locale);
String[] strings = javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key);
if (strings != null) {
if (!hasDuplicates(strings)) {
if (field == YEAR) {
if (strings.length > 0) {
map.put(strings[0], 1);
}
} else {
int base = (field == DAY_OF_WEEK) ? 1 : 0;
for (int i = 0; i < strings.length; i++) {
String name = strings[i];
// Ignore any empty string (some standalone month names
// are not defined)
if (name.length() == 0) {
continue;
}
map.put(name, base + i);
}
}
}
}
}
return map;
}
private static int getBaseStyle(int style) {
return style & ~(SHORT_STANDALONE - SHORT_FORMAT);
}
/**
* Comparator implementation for TreeMap which iterates keys from longest
* to shortest.
*/
private static class LengthBasedComparator implements Comparator<String> {
private static final LengthBasedComparator INSTANCE = new LengthBasedComparator();
private LengthBasedComparator() {
}
@Override
public int compare(String o1, String o2) {
int n = o2.length() - o1.length();
return (n == 0) ? o1.compareTo(o2) : n;
}
}
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
if (Locale.ROOT.equals(locale)) {
return true;
}
String calendarType = null;
if (locale.hasExtensions()) {
calendarType = locale.getUnicodeLocaleType("ca");
locale = locale.stripExtensions();
}
if (calendarType != null) {
switch (calendarType) {
case "buddhist":
case "japanese":
case "gregory":
case "islamic":
case "roc":
break;
default:
// Unknown calendar type
return false;
}
}
if (langtags.contains(locale.toLanguageTag())) {
return true;
}
if (type == LocaleProviderAdapter.Type.JRE) {
String oldname = locale.toString().replace('_', '-');
return langtags.contains(oldname);
}
return false;
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
private boolean hasDuplicates(String[] strings) {
int len = strings.length;
for (int i = 0; i < len - 1; i++) {
String a = strings[i];
if (a != null) {
for (int j = i + 1; j < len; j++) {
if (a.equals(strings[j])) {
return true;
}
}
}
}
return false;
}
private String getResourceKey(String type, int field, int style, boolean javatime) {
return getResourceKeyFor(this.type, type, field, style, javatime);
}
private static String getResourceKeyFor(LocaleProviderAdapter.Type adapterType,
String type, int field, int style, boolean javatime) {
int baseStyle = getBaseStyle(style);
boolean isStandalone = (style != baseStyle);
if ("gregory".equals(type)) {
type = null;
}
boolean isNarrow = (baseStyle == NARROW_FORMAT);
StringBuilder key = new StringBuilder();
// If javatime is true, use prefix "java.time.".
if (javatime) {
key.append("java.time.");
}
switch (field) {
case ERA:
if (type != null) {
key.append(type).append('.');
}
if (isNarrow) {
key.append("narrow.");
} else {
// JRE and CLDR use different resource key conventions
// due to historical reasons. (JRE DateFormatSymbols.getEras returns
// abbreviations while other getShort*() return abbreviations.)
if (adapterType == LocaleProviderAdapter.Type.JRE) {
if (javatime) {
if (baseStyle == LONG) {
key.append("long.");
}
}
if (baseStyle == SHORT) {
key.append("short.");
}
} else { // this.type == LocaleProviderAdapter.Type.CLDR
if (baseStyle == LONG) {
key.append("long.");
}
}
}
key.append("Eras");
break;
case YEAR:
if (!isNarrow) {
key.append(type).append(".FirstYear");
}
break;
case MONTH:
if ("islamic".equals(type)) {
key.append(type).append('.');
}
if (isStandalone) {
key.append("standalone.");
}
key.append("Month").append(toStyleName(baseStyle));
break;
case DAY_OF_WEEK:
// support standalone narrow day names
if (isStandalone && isNarrow) {
key.append("standalone.");
}
key.append("Day").append(toStyleName(baseStyle));
break;
case AM_PM:
if (isNarrow) {
key.append("narrow.");
}
key.append("AmPmMarkers");
break;
}
return key.length() > 0 ? key.toString() : null;
}
private static String toStyleName(int baseStyle) {
switch (baseStyle) {
case SHORT:
return "Abbreviations";
case NARROW_FORMAT:
return "Narrows";
}
return "Names";
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2013, 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.
*/
package sun.util.locale.provider;
import java.util.Calendar;
import java.util.Calendar.Builder;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import sun.util.spi.CalendarProvider;
/**
* Concrete implementation of the {@link sun.util.spi.CalendarProvider
* CalendarProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
*/
public class CalendarProviderImpl extends CalendarProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CalendarProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
// Support any locales.
return true;
}
/**
* Returns a new <code>Calendar</code> instance for the
* specified locale.
*
* @param zone the time zone
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a <code>Calendar</code> instance.
* @see java.util.Calendar#getInstance(java.util.Locale)
*/
@Override
public Calendar getInstance(TimeZone zone, Locale locale) {
return new Calendar.Builder()
.setLocale(locale)
.setTimeZone(zone)
.setInstant(System.currentTimeMillis())
.build();
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,277 @@
/*
* Copyright (c) 1996, 2012, 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.
*/
/*
* (C) Copyright Taligent, Inc. 1996,1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996, 1997 - All Rights Reserved
*
* The original version of this source code and documentation is copyrighted
* and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
* materials are provided under terms of a License Agreement between Taligent
* and Sun. This technology is protected by multiple US and International
* patents. This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package sun.util.locale.provider;
/**
* CollationRules contains the default en_US collation rules as a base
* for building other collation tables.
* <p>Note that decompositions are done before these rules are used,
* so they do not have to contain accented characters, such as A-grave.
* @see RuleBasedCollator
* @see LocaleElements
* @author Helena Shih, Mark Davis
*/
final class CollationRules {
final static String DEFAULTRULES =
"" // no FRENCH accent order by default, add in French Delta
// IGNORABLES (up to first < character)
// COMPLETELY IGNORE format characters
+ "='\u200B'=\u200C=\u200D=\u200E=\u200F"
// Control Characters
+ "=\u0000 =\u0001 =\u0002 =\u0003 =\u0004" //null, .. eot
+ "=\u0005 =\u0006 =\u0007 =\u0008 ='\u0009'" //enq, ...
+ "='\u000b' =\u000e" //vt,, so
+ "=\u000f ='\u0010' =\u0011 =\u0012 =\u0013" //si, dle, dc1, dc2, dc3
+ "=\u0014 =\u0015 =\u0016 =\u0017 =\u0018" //dc4, nak, syn, etb, can
+ "=\u0019 =\u001a =\u001b =\u001c =\u001d" //em, sub, esc, fs, gs
+ "=\u001e =\u001f =\u007f" //rs, us, del
//....then the C1 Latin 1 reserved control codes
+ "=\u0080 =\u0081 =\u0082 =\u0083 =\u0084 =\u0085"
+ "=\u0086 =\u0087 =\u0088 =\u0089 =\u008a =\u008b"
+ "=\u008c =\u008d =\u008e =\u008f =\u0090 =\u0091"
+ "=\u0092 =\u0093 =\u0094 =\u0095 =\u0096 =\u0097"
+ "=\u0098 =\u0099 =\u009a =\u009b =\u009c =\u009d"
+ "=\u009e =\u009f"
// IGNORE except for secondary, tertiary difference
// Spaces
+ ";'\u0020';'\u00A0'" // spaces
+ ";'\u2000';'\u2001';'\u2002';'\u2003';'\u2004'" // spaces
+ ";'\u2005';'\u2006';'\u2007';'\u2008';'\u2009'" // spaces
+ ";'\u200A';'\u3000';'\uFEFF'" // spaces
+ ";'\r' ;'\t' ;'\n';'\f';'\u000b'" // whitespace
// Non-spacing accents
+ ";\u0301" // non-spacing acute accent
+ ";\u0300" // non-spacing grave accent
+ ";\u0306" // non-spacing breve accent
+ ";\u0302" // non-spacing circumflex accent
+ ";\u030c" // non-spacing caron/hacek accent
+ ";\u030a" // non-spacing ring above accent
+ ";\u030d" // non-spacing vertical line above
+ ";\u0308" // non-spacing diaeresis accent
+ ";\u030b" // non-spacing double acute accent
+ ";\u0303" // non-spacing tilde accent
+ ";\u0307" // non-spacing dot above/overdot accent
+ ";\u0304" // non-spacing macron accent
+ ";\u0337" // non-spacing short slash overlay (overstruck diacritic)
+ ";\u0327" // non-spacing cedilla accent
+ ";\u0328" // non-spacing ogonek accent
+ ";\u0323" // non-spacing dot-below/underdot accent
+ ";\u0332" // non-spacing underscore/underline accent
// with the rest of the general diacritical marks in binary order
+ ";\u0305" // non-spacing overscore/overline
+ ";\u0309" // non-spacing hook above
+ ";\u030e" // non-spacing double vertical line above
+ ";\u030f" // non-spacing double grave
+ ";\u0310" // non-spacing chandrabindu
+ ";\u0311" // non-spacing inverted breve
+ ";\u0312" // non-spacing turned comma above/cedilla above
+ ";\u0313" // non-spacing comma above
+ ";\u0314" // non-spacing reversed comma above
+ ";\u0315" // non-spacing comma above right
+ ";\u0316" // non-spacing grave below
+ ";\u0317" // non-spacing acute below
+ ";\u0318" // non-spacing left tack below
+ ";\u0319" // non-spacing tack below
+ ";\u031a" // non-spacing left angle above
+ ";\u031b" // non-spacing horn
+ ";\u031c" // non-spacing left half ring below
+ ";\u031d" // non-spacing up tack below
+ ";\u031e" // non-spacing down tack below
+ ";\u031f" // non-spacing plus sign below
+ ";\u0320" // non-spacing minus sign below
+ ";\u0321" // non-spacing palatalized hook below
+ ";\u0322" // non-spacing retroflex hook below
+ ";\u0324" // non-spacing double dot below
+ ";\u0325" // non-spacing ring below
+ ";\u0326" // non-spacing comma below
+ ";\u0329" // non-spacing vertical line below
+ ";\u032a" // non-spacing bridge below
+ ";\u032b" // non-spacing inverted double arch below
+ ";\u032c" // non-spacing hacek below
+ ";\u032d" // non-spacing circumflex below
+ ";\u032e" // non-spacing breve below
+ ";\u032f" // non-spacing inverted breve below
+ ";\u0330" // non-spacing tilde below
+ ";\u0331" // non-spacing macron below
+ ";\u0333" // non-spacing double underscore
+ ";\u0334" // non-spacing tilde overlay
+ ";\u0335" // non-spacing short bar overlay
+ ";\u0336" // non-spacing long bar overlay
+ ";\u0338" // non-spacing long slash overlay
+ ";\u0339" // non-spacing right half ring below
+ ";\u033a" // non-spacing inverted bridge below
+ ";\u033b" // non-spacing square below
+ ";\u033c" // non-spacing seagull below
+ ";\u033d" // non-spacing x above
+ ";\u033e" // non-spacing vertical tilde
+ ";\u033f" // non-spacing double overscore
//+ ";\u0340" // non-spacing grave tone mark == \u0300
//+ ";\u0341" // non-spacing acute tone mark == \u0301
+ ";\u0342;"
//+ "\u0343;" // == \u0313
+ "\u0344;\u0345;\u0360;\u0361" // newer
+ ";\u0483;\u0484;\u0485;\u0486" // Cyrillic accents
+ ";\u20D0;\u20D1;\u20D2" // symbol accents
+ ";\u20D3;\u20D4;\u20D5" // symbol accents
+ ";\u20D6;\u20D7;\u20D8" // symbol accents
+ ";\u20D9;\u20DA;\u20DB" // symbol accents
+ ";\u20DC;\u20DD;\u20DE" // symbol accents
+ ";\u20DF;\u20E0;\u20E1" // symbol accents
+ ",'\u002D';\u00AD" // dashes
+ ";\u2010;\u2011;\u2012" // dashes
+ ";\u2013;\u2014;\u2015" // dashes
+ ";\u2212" // dashes
// other punctuation
+ "<'\u005f'" // underline/underscore (spacing)
+ "<\u00af" // overline or macron (spacing)
+ "<'\u002c'" // comma (spacing)
+ "<'\u003b'" // semicolon
+ "<'\u003a'" // colon
+ "<'\u0021'" // exclamation point
+ "<\u00a1" // inverted exclamation point
+ "<'\u003f'" // question mark
+ "<\u00bf" // inverted question mark
+ "<'\u002f'" // slash
+ "<'\u002e'" // period/full stop
+ "<\u00b4" // acute accent (spacing)
+ "<'\u0060'" // grave accent (spacing)
+ "<'\u005e'" // circumflex accent (spacing)
+ "<\u00a8" // diaresis/umlaut accent (spacing)
+ "<'\u007e'" // tilde accent (spacing)
+ "<\u00b7" // middle dot (spacing)
+ "<\u00b8" // cedilla accent (spacing)
+ "<'\u0027'" // apostrophe
+ "<'\"'" // quotation marks
+ "<\u00ab" // left angle quotes
+ "<\u00bb" // right angle quotes
+ "<'\u0028'" // left parenthesis
+ "<'\u0029'" // right parenthesis
+ "<'\u005b'" // left bracket
+ "<'\u005d'" // right bracket
+ "<'\u007b'" // left brace
+ "<'\u007d'" // right brace
+ "<\u00a7" // section symbol
+ "<\u00b6" // paragraph symbol
+ "<\u00a9" // copyright symbol
+ "<\u00ae" // registered trademark symbol
+ "<'\u0040'" // at sign
+ "<\u00a4" // international currency symbol
+ "<\u0e3f" // baht sign
+ "<\u00a2" // cent sign
+ "<\u20a1" // colon sign
+ "<\u20a2" // cruzeiro sign
+ "<'\u0024'" // dollar sign
+ "<\u20ab" // dong sign
+ "<\u20ac" // euro sign
+ "<\u20a3" // franc sign
+ "<\u20a4" // lira sign
+ "<\u20a5" // mill sign
+ "<\u20a6" // naira sign
+ "<\u20a7" // peseta sign
+ "<\u00a3" // pound-sterling sign
+ "<\u20a8" // rupee sign
+ "<\u20aa" // new shekel sign
+ "<\u20a9" // won sign
+ "<\u00a5" // yen sign
+ "<'\u002a'" // asterisk
+ "<'\\'" // backslash
+ "<'\u0026'" // ampersand
+ "<'\u0023'" // number sign
+ "<'\u0025'" // percent sign
+ "<'\u002b'" // plus sign
+ "<\u00b1" // plus-or-minus sign
+ "<\u00f7" // divide sign
+ "<\u00d7" // multiply sign
+ "<'\u003c'" // less-than sign
+ "<'\u003d'" // equal sign
+ "<'\u003e'" // greater-than sign
+ "<\u00ac" // end of line symbol/logical NOT symbol
+ "<'\u007c'" // vertical line/logical OR symbol
+ "<\u00a6" // broken vertical line
+ "<\u00b0" // degree symbol
+ "<\u00b5" // micro symbol
// NUMERICS
+ "<0<1<2<3<4<5<6<7<8<9"
+ "<\u00bc<\u00bd<\u00be" // 1/4,1/2,3/4 fractions
// NON-IGNORABLES
+ "<a,A"
+ "<b,B"
+ "<c,C"
+ "<d,D"
+ "<\u00F0,\u00D0" // eth
+ "<e,E"
+ "<f,F"
+ "<g,G"
+ "<h,H"
+ "<i,I"
+ "<j,J"
+ "<k,K"
+ "<l,L"
+ "<m,M"
+ "<n,N"
+ "<o,O"
+ "<p,P"
+ "<q,Q"
+ "<r,R"
+ "<s, S & SS,\u00DF" // s-zet
+ "<t,T"
+ "& TH, \u00DE &TH, \u00FE " // thorn
+ "<u,U"
+ "<v,V"
+ "<w,W"
+ "<x,X"
+ "<y,Y"
+ "<z,Z"
+ "&AE,\u00C6" // ae & AE ligature
+ "&AE,\u00E6"
+ "&OE,\u0152" // oe & OE ligature
+ "&OE,\u0153";
// No instantiation
private CollationRules() {
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 1999, 2013, 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.
*/
/*
*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package sun.util.locale.provider;
import java.text.Collator;
import java.text.ParseException;
import java.text.RuleBasedCollator;
import java.text.spi.CollatorProvider;
import java.util.Locale;
import java.util.Set;
/**
* Concrete implementation of the
* {@link java.text.spi.CollatorProvider CollatorProvider} class
* for the JRE LocaleProviderAdapter.
*/
public class CollatorProviderImpl extends CollatorProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CollatorProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>Collator</code> instance for the specified locale.
* @param locale the desired locale.
* @return the <code>Collator</code> for the desired locale.
* @exception NullPointerException if
* <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.text.Collator#getInstance(java.util.Locale)
*/
@Override
public Collator getInstance(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
Collator result = null;
// Load the resource of the desired locale from resource
// manager.
String colString = LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCollationData();
try
{
result = new RuleBasedCollator(CollationRules.DEFAULTRULES +
colString);
}
catch(ParseException foo)
{
// predefined tables should contain correct grammar
try {
result = new RuleBasedCollator(CollationRules.DEFAULTRULES);
} catch (ParseException bar) {
// the default rules should always be parsable.
throw new InternalError(bar);
}
}
// Now that RuleBasedCollator adds expansions for pre-composed characters
// into their decomposed equivalents, the default collators don't need
// to have decomposition turned on. Laura, 5/5/98, bug 4114077
result.setDecomposition(Collator.NO_DECOMPOSITION);
return (Collator)result.clone();
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
package sun.util.locale.provider;
import java.util.Locale;
import java.util.Set;
import java.util.spi.CurrencyNameProvider;
/**
* Concrete implementation of the
* {@link java.util.spi.CurrencyNameProvider CurrencyNameProvider} class
* for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class CurrencyNameProviderImpl extends CurrencyNameProvider
implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public CurrencyNameProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
/**
* Gets the symbol of the given currency code for the specified locale.
* For example, for "USD" (US Dollar), the symbol is "$" if the specified
* locale is the US, while for other locales it may be "US$". If no
* symbol can be determined, null should be returned.
*
* @param currencyCode the ISO 4217 currency code, which
* consists of three upper-case letters between 'A' (U+0041) and
* 'Z' (U+005A)
* @param locale the desired locale
* @return the symbol of the given currency code for the specified locale, or null if
* the symbol is not available for the locale
* @exception NullPointerException if <code>currencyCode</code> or
* <code>locale</code> is null
* @exception IllegalArgumentException if <code>currencyCode</code> is not in
* the form of three upper-case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Currency#getSymbol(java.util.Locale)
*/
@Override
public String getSymbol(String currencyCode, Locale locale) {
return getString(currencyCode.toUpperCase(Locale.ROOT), locale);
}
/**
* Returns a name for the currency that is appropriate for display to the
* user. The default implementation returns null.
*
* @param currencyCode the ISO 4217 currency code, which
* consists of three upper-case letters between 'A' (U+0041) and
* 'Z' (U+005A)
* @param locale the desired locale
* @return the name for the currency that is appropriate for display to the
* user, or null if the name is not available for the locale
* @exception IllegalArgumentException if <code>currencyCode</code> is not in
* the form of three upper-case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>currencyCode</code> or
* <code>locale</code> is <code>null</code>
* @since 1.7
*/
@Override
public String getDisplayName(String currencyCode, Locale locale) {
return getString(currencyCode.toLowerCase(Locale.ROOT), locale);
}
private String getString(String key, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCurrencyName(key);
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 1999, 2012, 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.
*/
package sun.util.locale.provider;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.spi.DateFormatProvider;
import java.util.Calendar;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.DateFormatProvider
* DateFormatProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class DateFormatProviderImpl extends DateFormatProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public DateFormatProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>DateFormat</code> instance which formats time
* with the given formatting style for the specified locale.
* @param style the given formatting style. Either one of
* {@link java.text.DateFormat#SHORT DateFormat.SHORT},
* {@link java.text.DateFormat#MEDIUM DateFormat.MEDIUM},
* {@link java.text.DateFormat#LONG DateFormat.LONG}, or
* {@link java.text.DateFormat#FULL DateFormat.FULL}.
* @param locale the desired locale.
* @exception IllegalArgumentException if <code>style</code> is invalid,
* or if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>locale</code> is null
* @return a time formatter.
* @see java.text.DateFormat#getTimeInstance(int, java.util.Locale)
*/
@Override
public DateFormat getTimeInstance(int style, Locale locale) {
return getInstance(-1, style, locale);
}
/**
* Returns a new <code>DateFormat</code> instance which formats date
* with the given formatting style for the specified locale.
* @param style the given formatting style. Either one of
* {@link java.text.DateFormat#SHORT DateFormat.SHORT},
* {@link java.text.DateFormat#MEDIUM DateFormat.MEDIUM},
* {@link java.text.DateFormat#LONG DateFormat.LONG}, or
* {@link java.text.DateFormat#FULL DateFormat.FULL}.
* @param locale the desired locale.
* @exception IllegalArgumentException if <code>style</code> is invalid,
* or if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>locale</code> is null
* @return a date formatter.
* @see java.text.DateFormat#getDateInstance(int, java.util.Locale)
*/
@Override
public DateFormat getDateInstance(int style, Locale locale) {
return getInstance(style, -1, locale);
}
/**
* Returns a new <code>DateFormat</code> instance which formats date and time
* with the given formatting style for the specified locale.
* @param dateStyle the given date formatting style. Either one of
* {@link java.text.DateFormat#SHORT DateFormat.SHORT},
* {@link java.text.DateFormat#MEDIUM DateFormat.MEDIUM},
* {@link java.text.DateFormat#LONG DateFormat.LONG}, or
* {@link java.text.DateFormat#FULL DateFormat.FULL}.
* @param timeStyle the given time formatting style. Either one of
* {@link java.text.DateFormat#SHORT DateFormat.SHORT},
* {@link java.text.DateFormat#MEDIUM DateFormat.MEDIUM},
* {@link java.text.DateFormat#LONG DateFormat.LONG}, or
* {@link java.text.DateFormat#FULL DateFormat.FULL}.
* @param locale the desired locale.
* @exception IllegalArgumentException if <code>dateStyle</code> or
* <code>timeStyle</code> is invalid,
* or if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>locale</code> is null
* @return a date/time formatter.
* @see java.text.DateFormat#getDateTimeInstance(int, int, java.util.Locale)
*/
@Override
public DateFormat getDateTimeInstance(int dateStyle, int timeStyle,
Locale locale) {
return getInstance(dateStyle, timeStyle, locale);
}
private DateFormat getInstance(int dateStyle, int timeStyle, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
SimpleDateFormat sdf = new SimpleDateFormat("", locale);
Calendar cal = sdf.getCalendar();
try {
String pattern = LocaleProviderAdapter.forType(type)
.getLocaleResources(locale).getDateTimePattern(timeStyle, dateStyle,
cal);
sdf.applyPattern(pattern);
} catch (MissingResourceException mre) {
// Specify the fallback pattern
sdf.applyPattern("M/d/yy h:mm a");
}
return sdf;
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 1999, 2012, 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.
*/
package sun.util.locale.provider;
import java.text.DateFormatSymbols;
import java.text.spi.DateFormatSymbolsProvider;
import java.util.Locale;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.DateFormatSymbolsProvider
* DateFormatSymbolsProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class DateFormatSymbolsProviderImpl extends DateFormatSymbolsProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public DateFormatSymbolsProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>DateFormatSymbols</code> instance for the
* specified locale.
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a <code>DateFormatSymbols</code> instance.
* @see java.text.DateFormatSymbols#getInstance(java.util.Locale)
*/
@Override
public DateFormatSymbols getInstance(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
return new DateFormatSymbols(locale);
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 1999, 2012, 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.
*/
package sun.util.locale.provider;
import java.text.DecimalFormatSymbols;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.util.Locale;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.DecimalFormatSymbolsProvider
* DecimalFormatSymbolsProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class DecimalFormatSymbolsProviderImpl extends DecimalFormatSymbolsProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public DecimalFormatSymbolsProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>DecimalFormatSymbols</code> instance for the
* specified locale.
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a <code>DecimalFormatSymbols</code> instance.
* @see java.text.DecimalFormatSymbols#getInstance(java.util.Locale)
*/
@Override
public DecimalFormatSymbols getInstance(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
return new DecimalFormatSymbols(locale);
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,525 @@
/*
* Copyright (c) 1999, 2012, 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.
*/
/*
*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package sun.util.locale.provider;
import java.io.IOException;
import java.text.CharacterIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* A subclass of RuleBasedBreakIterator that adds the ability to use a dictionary
* to further subdivide ranges of text beyond what is possible using just the
* state-table-based algorithm. This is necessary, for example, to handle
* word and line breaking in Thai, which doesn't use spaces between words. The
* state-table-based algorithm used by RuleBasedBreakIterator is used to divide
* up text as far as possible, and then contiguous ranges of letters are
* repeatedly compared against a list of known words (i.e., the dictionary)
* to divide them up into words.
*
* DictionaryBasedBreakIterator uses the same rule language as RuleBasedBreakIterator,
* but adds one more special substitution name: &lt;dictionary&gt;. This substitution
* name is used to identify characters in words in the dictionary. The idea is that
* if the iterator passes over a chunk of text that includes two or more characters
* in a row that are included in &lt;dictionary&gt;, it goes back through that range and
* derives additional break positions (if possible) using the dictionary.
*
* DictionaryBasedBreakIterator is also constructed with the filename of a dictionary
* file. It follows a prescribed search path to locate the dictionary (right now,
* it looks for it in /com/ibm/text/resources in each directory in the classpath,
* and won't find it in JAR files, but this location is likely to change). The
* dictionary file is in a serialized binary format. We have a very primitive (and
* slow) BuildDictionaryFile utility for creating dictionary files, but aren't
* currently making it public. Contact us for help.
*/
class DictionaryBasedBreakIterator extends RuleBasedBreakIterator {
/**
* a list of known words that is used to divide up contiguous ranges of letters,
* stored in a compressed, indexed, format that offers fast access
*/
private BreakDictionary dictionary;
/**
* a list of flags indicating which character categories are contained in
* the dictionary file (this is used to determine which ranges of characters
* to apply the dictionary to)
*/
private boolean[] categoryFlags;
/**
* a temporary hiding place for the number of dictionary characters in the
* last range passed over by next()
*/
private int dictionaryCharCount;
/**
* when a range of characters is divided up using the dictionary, the break
* positions that are discovered are stored here, preventing us from having
* to use either the dictionary or the state table again until the iterator
* leaves this range of text
*/
private int[] cachedBreakPositions;
/**
* if cachedBreakPositions is not null, this indicates which item in the
* cache the current iteration position refers to
*/
private int positionInCache;
/**
* Constructs a DictionaryBasedBreakIterator.
* @param description Same as the description parameter on RuleBasedBreakIterator,
* except for the special meaning of "<dictionary>". This parameter is just
* passed through to RuleBasedBreakIterator's constructor.
* @param dictionaryFilename The filename of the dictionary file to use
*/
DictionaryBasedBreakIterator(String dataFile, String dictionaryFile)
throws IOException {
super(dataFile);
byte[] tmp = super.getAdditionalData();
if (tmp != null) {
prepareCategoryFlags(tmp);
super.setAdditionalData(null);
}
dictionary = new BreakDictionary(dictionaryFile);
}
private void prepareCategoryFlags(byte[] data) {
categoryFlags = new boolean[data.length];
for (int i = 0; i < data.length; i++) {
categoryFlags[i] = (data[i] == (byte)1) ? true : false;
}
}
@Override
public void setText(CharacterIterator newText) {
super.setText(newText);
cachedBreakPositions = null;
dictionaryCharCount = 0;
positionInCache = 0;
}
/**
* Sets the current iteration position to the beginning of the text.
* (i.e., the CharacterIterator's starting offset).
* @return The offset of the beginning of the text.
*/
@Override
public int first() {
cachedBreakPositions = null;
dictionaryCharCount = 0;
positionInCache = 0;
return super.first();
}
/**
* Sets the current iteration position to the end of the text.
* (i.e., the CharacterIterator's ending offset).
* @return The text's past-the-end offset.
*/
@Override
public int last() {
cachedBreakPositions = null;
dictionaryCharCount = 0;
positionInCache = 0;
return super.last();
}
/**
* Advances the iterator one step backwards.
* @return The position of the last boundary position before the
* current iteration position
*/
@Override
public int previous() {
CharacterIterator text = getText();
// if we have cached break positions and we're still in the range
// covered by them, just move one step backward in the cache
if (cachedBreakPositions != null && positionInCache > 0) {
--positionInCache;
text.setIndex(cachedBreakPositions[positionInCache]);
return cachedBreakPositions[positionInCache];
}
// otherwise, dump the cache and use the inherited previous() method to move
// backward. This may fill up the cache with new break positions, in which
// case we have to mark our position in the cache
else {
cachedBreakPositions = null;
int result = super.previous();
if (cachedBreakPositions != null) {
positionInCache = cachedBreakPositions.length - 2;
}
return result;
}
}
/**
* Sets the current iteration position to the last boundary position
* before the specified position.
* @param offset The position to begin searching from
* @return The position of the last boundary before "offset"
*/
@Override
public int preceding(int offset) {
CharacterIterator text = getText();
checkOffset(offset, text);
// if we have no cached break positions, or "offset" is outside the
// range covered by the cache, we can just call the inherited routine
// (which will eventually call other routines in this class that may
// refresh the cache)
if (cachedBreakPositions == null || offset <= cachedBreakPositions[0] ||
offset > cachedBreakPositions[cachedBreakPositions.length - 1]) {
cachedBreakPositions = null;
return super.preceding(offset);
}
// on the other hand, if "offset" is within the range covered by the cache,
// then all we have to do is search the cache for the last break position
// before "offset"
else {
positionInCache = 0;
while (positionInCache < cachedBreakPositions.length
&& offset > cachedBreakPositions[positionInCache]) {
++positionInCache;
}
--positionInCache;
text.setIndex(cachedBreakPositions[positionInCache]);
return text.getIndex();
}
}
/**
* Sets the current iteration position to the first boundary position after
* the specified position.
* @param offset The position to begin searching forward from
* @return The position of the first boundary after "offset"
*/
@Override
public int following(int offset) {
CharacterIterator text = getText();
checkOffset(offset, text);
// if we have no cached break positions, or if "offset" is outside the
// range covered by the cache, then dump the cache and call our
// inherited following() method. This will call other methods in this
// class that may refresh the cache.
if (cachedBreakPositions == null || offset < cachedBreakPositions[0] ||
offset >= cachedBreakPositions[cachedBreakPositions.length - 1]) {
cachedBreakPositions = null;
return super.following(offset);
}
// on the other hand, if "offset" is within the range covered by the
// cache, then just search the cache for the first break position
// after "offset"
else {
positionInCache = 0;
while (positionInCache < cachedBreakPositions.length
&& offset >= cachedBreakPositions[positionInCache]) {
++positionInCache;
}
text.setIndex(cachedBreakPositions[positionInCache]);
return text.getIndex();
}
}
/**
* This is the implementation function for next().
*/
@Override
protected int handleNext() {
CharacterIterator text = getText();
// if there are no cached break positions, or if we've just moved
// off the end of the range covered by the cache, we have to dump
// and possibly regenerate the cache
if (cachedBreakPositions == null ||
positionInCache == cachedBreakPositions.length - 1) {
// start by using the inherited handleNext() to find a tentative return
// value. dictionaryCharCount tells us how many dictionary characters
// we passed over on our way to the tentative return value
int startPos = text.getIndex();
dictionaryCharCount = 0;
int result = super.handleNext();
// if we passed over more than one dictionary character, then we use
// divideUpDictionaryRange() to regenerate the cached break positions
// for the new range
if (dictionaryCharCount > 1 && result - startPos > 1) {
divideUpDictionaryRange(startPos, result);
}
// otherwise, the value we got back from the inherited fuction
// is our return value, and we can dump the cache
else {
cachedBreakPositions = null;
return result;
}
}
// if the cache of break positions has been regenerated (or existed all
// along), then just advance to the next break position in the cache
// and return it
if (cachedBreakPositions != null) {
++positionInCache;
text.setIndex(cachedBreakPositions[positionInCache]);
return cachedBreakPositions[positionInCache];
}
return -9999; // SHOULD NEVER GET HERE!
}
/**
* Looks up a character category for a character.
*/
@Override
protected int lookupCategory(int c) {
// this override of lookupCategory() exists only to keep track of whether we've
// passed over any dictionary characters. It calls the inherited lookupCategory()
// to do the real work, and then checks whether its return value is one of the
// categories represented in the dictionary. If it is, bump the dictionary-
// character count.
int result = super.lookupCategory(c);
if (result != RuleBasedBreakIterator.IGNORE && categoryFlags[result]) {
++dictionaryCharCount;
}
return result;
}
/**
* This is the function that actually implements the dictionary-based
* algorithm. Given the endpoints of a range of text, it uses the
* dictionary to determine the positions of any boundaries in this
* range. It stores all the boundary positions it discovers in
* cachedBreakPositions so that we only have to do this work once
* for each time we enter the range.
*/
@SuppressWarnings("unchecked")
private void divideUpDictionaryRange(int startPos, int endPos) {
CharacterIterator text = getText();
// the range we're dividing may begin or end with non-dictionary characters
// (i.e., for line breaking, we may have leading or trailing punctuation
// that needs to be kept with the word). Seek from the beginning of the
// range to the first dictionary character
text.setIndex(startPos);
int c = getCurrent();
int category = lookupCategory(c);
while (category == IGNORE || !categoryFlags[category]) {
c = getNext();
category = lookupCategory(c);
}
// initialize. We maintain two stacks: currentBreakPositions contains
// the list of break positions that will be returned if we successfully
// finish traversing the whole range now. possibleBreakPositions lists
// all other possible word ends we've passed along the way. (Whenever
// we reach an error [a sequence of characters that can't begin any word
// in the dictionary], we back up, possibly delete some breaks from
// currentBreakPositions, move a break from possibleBreakPositions
// to currentBreakPositions, and start over from there. This process
// continues in this way until we either successfully make it all the way
// across the range, or exhaust all of our combinations of break
// positions.)
Stack<Integer> currentBreakPositions = new Stack<>();
Stack<Integer> possibleBreakPositions = new Stack<>();
List<Integer> wrongBreakPositions = new ArrayList<>();
// the dictionary is implemented as a trie, which is treated as a state
// machine. -1 represents the end of a legal word. Every word in the
// dictionary is represented by a path from the root node to -1. A path
// that ends in state 0 is an illegal combination of characters.
int state = 0;
// these two variables are used for error handling. We keep track of the
// farthest we've gotten through the range being divided, and the combination
// of breaks that got us that far. If we use up all possible break
// combinations, the text contains an error or a word that's not in the
// dictionary. In this case, we "bless" the break positions that got us the
// farthest as real break positions, and then start over from scratch with
// the character where the error occurred.
int farthestEndPoint = text.getIndex();
Stack<Integer> bestBreakPositions = null;
// initialize (we always exit the loop with a break statement)
c = getCurrent();
while (true) {
// if we can transition to state "-1" from our current state, we're
// on the last character of a legal word. Push that position onto
// the possible-break-positions stack
if (dictionary.getNextState(state, 0) == -1) {
possibleBreakPositions.push(text.getIndex());
}
// look up the new state to transition to in the dictionary
state = dictionary.getNextStateFromCharacter(state, c);
// if the character we're sitting on causes us to transition to
// the "end of word" state, then it was a non-dictionary character
// and we've successfully traversed the whole range. Drop out
// of the loop.
if (state == -1) {
currentBreakPositions.push(text.getIndex());
break;
}
// if the character we're sitting on causes us to transition to
// the error state, or if we've gone off the end of the range
// without transitioning to the "end of word" state, we've hit
// an error...
else if (state == 0 || text.getIndex() >= endPos) {
// if this is the farthest we've gotten, take note of it in
// case there's an error in the text
if (text.getIndex() > farthestEndPoint) {
farthestEndPoint = text.getIndex();
@SuppressWarnings("unchecked")
Stack<Integer> currentBreakPositionsCopy = (Stack<Integer>) currentBreakPositions.clone();
bestBreakPositions = currentBreakPositionsCopy;
}
// wrongBreakPositions is a list of all break positions
// we've tried starting that didn't allow us to traverse
// all the way through the text. Every time we pop a
// break position off of currentBreakPositions, we put it
// into wrongBreakPositions to avoid trying it again later.
// If we make it to this spot, we're either going to back
// up to a break in possibleBreakPositions and try starting
// over from there, or we've exhausted all possible break
// positions and are going to do the fallback procedure.
// This loop prevents us from messing with anything in
// possibleBreakPositions that didn't work as a starting
// point the last time we tried it (this is to prevent a bunch of
// repetitive checks from slowing down some extreme cases)
while (!possibleBreakPositions.isEmpty()
&& wrongBreakPositions.contains(possibleBreakPositions.peek())) {
possibleBreakPositions.pop();
}
// if we've used up all possible break-position combinations, there's
// an error or an unknown word in the text. In this case, we start
// over, treating the farthest character we've reached as the beginning
// of the range, and "blessing" the break positions that got us that
// far as real break positions
if (possibleBreakPositions.isEmpty()) {
if (bestBreakPositions != null) {
currentBreakPositions = bestBreakPositions;
if (farthestEndPoint < endPos) {
text.setIndex(farthestEndPoint + 1);
}
else {
break;
}
}
else {
if ((currentBreakPositions.size() == 0 ||
currentBreakPositions.peek().intValue() != text.getIndex())
&& text.getIndex() != startPos) {
currentBreakPositions.push(new Integer(text.getIndex()));
}
getNext();
currentBreakPositions.push(new Integer(text.getIndex()));
}
}
// if we still have more break positions we can try, then promote the
// last break in possibleBreakPositions into currentBreakPositions,
// and get rid of all entries in currentBreakPositions that come after
// it. Then back up to that position and start over from there (i.e.,
// treat that position as the beginning of a new word)
else {
Integer temp = possibleBreakPositions.pop();
Integer temp2 = null;
while (!currentBreakPositions.isEmpty() && temp.intValue() <
currentBreakPositions.peek().intValue()) {
temp2 = currentBreakPositions.pop();
wrongBreakPositions.add(temp2);
}
currentBreakPositions.push(temp);
text.setIndex(currentBreakPositions.peek().intValue());
}
// re-sync "c" for the next go-round, and drop out of the loop if
// we've made it off the end of the range
c = getCurrent();
if (text.getIndex() >= endPos) {
break;
}
}
// if we didn't hit any exceptional conditions on this last iteration,
// just advance to the next character and loop
else {
c = getNext();
}
}
// dump the last break position in the list, and replace it with the actual
// end of the range (which may be the same character, or may be further on
// because the range actually ended with non-dictionary characters we want to
// keep with the word)
if (!currentBreakPositions.isEmpty()) {
currentBreakPositions.pop();
}
currentBreakPositions.push(endPos);
// create a regular array to hold the break positions and copy
// the break positions from the stack to the array (in addition,
// our starting position goes into this array as a break position).
// This array becomes the cache of break positions used by next()
// and previous(), so this is where we actually refresh the cache.
cachedBreakPositions = new int[currentBreakPositions.size() + 1];
cachedBreakPositions[0] = startPos;
for (int i = 0; i < currentBreakPositions.size(); i++) {
cachedBreakPositions[i + 1] = currentBreakPositions.elementAt(i).intValue();
}
positionInCache = 0;
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
package sun.util.locale.provider;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
/**
* FallbackProviderAdapter implementation.
*
* @author Naoto Sato
*/
public class FallbackLocaleProviderAdapter extends JRELocaleProviderAdapter {
/**
* Supported language tag set.
*/
private static final Set<String> rootTagSet =
Collections.singleton(Locale.ROOT.toLanguageTag());
/**
* Fallback provider only provides the ROOT locale data.
*/
private final LocaleResources rootLocaleResources =
new LocaleResources(this, Locale.ROOT);
/**
* Returns the type of this LocaleProviderAdapter
*/
@Override
public LocaleProviderAdapter.Type getAdapterType() {
return Type.FALLBACK;
}
@Override
public LocaleResources getLocaleResources(Locale locale) {
return rootLocaleResources;
}
@Override
protected Set<String> createLanguageTagSet(String category) {
return rootTagSet;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2012, 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.
*/
package sun.util.locale.provider;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.spi.LocaleServiceProvider;
/**
* LocaleProviderAdapter implementation for the host locale data.
* Currently it is only implemented on Windows Vista or later.
*
* @author Naoto Sato
*/
public class HostLocaleProviderAdapter extends AuxLocaleProviderAdapter {
/**
* Returns the type of this LocaleProviderAdapter
*/
@Override
public LocaleProviderAdapter.Type getAdapterType() {
return LocaleProviderAdapter.Type.HOST;
}
@Override
@SuppressWarnings("unchecked")
protected <P extends LocaleServiceProvider> P findInstalledProvider(final Class<P> c) {
try {
Method getter = HostLocaleProviderAdapterImpl.class.getMethod(
"get" + c.getSimpleName(), (Class<?>[]) null);
return (P)getter.invoke(null, (Object[]) null);
} catch (NoSuchMethodException |
IllegalAccessException |
IllegalArgumentException |
InvocationTargetException ex) {
LocaleServiceProviderPool.config(HostLocaleProviderAdapter.class, ex.toString());
}
return null;
}
}

View File

@@ -0,0 +1,645 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
package sun.util.locale.provider;
import java.lang.ref.SoftReference;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Calendar;
import java.util.Collections;
import java.util.Currency;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle.Control;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import sun.util.spi.CalendarProvider;
/**
* LocaleProviderdapter implementation for the Windows locale data.
*
* @author Naoto Sato
*/
public class HostLocaleProviderAdapterImpl {
// locale categories
private static final int CAT_DISPLAY = 0;
private static final int CAT_FORMAT = 1;
// NumberFormat styles
private static final int NF_NUMBER = 0;
private static final int NF_CURRENCY = 1;
private static final int NF_PERCENT = 2;
private static final int NF_INTEGER = 3;
private static final int NF_MAX = NF_INTEGER;
// CalendarData value types
private static final int CD_FIRSTDAYOFWEEK = 0;
private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
// Currency/Locale display name types
private static final int DN_CURRENCY_NAME = 0;
private static final int DN_CURRENCY_SYMBOL = 1;
private static final int DN_LOCALE_LANGUAGE = 2;
private static final int DN_LOCALE_SCRIPT = 3;
private static final int DN_LOCALE_REGION = 4;
private static final int DN_LOCALE_VARIANT = 5;
// Native Calendar ID to LDML calendar type map
private static final String[] calIDToLDML = {
"",
"gregory",
"gregory_en-US",
"japanese",
"roc",
"", // No appropriate type for CAL_KOREA
"islamic",
"buddhist",
"hebrew",
"gregory_fr",
"gregory_ar",
"gregory_en",
"gregory_fr",
};
// Caches
private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatCache = new ConcurrentHashMap<>();
private static ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsCache = new ConcurrentHashMap<>();
private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatCache = new ConcurrentHashMap<>();
private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsCache = new ConcurrentHashMap<>();
private static final Set<Locale> supportedLocaleSet;
private static final String nativeDisplayLanguage;
static {
Set<Locale> tmpSet = new HashSet<>();
if (initialize()) {
// Assuming the default locales do not include any extensions, so
// no stripping is needed here.
Control c = Control.getNoFallbackControl(Control.FORMAT_DEFAULT);
String displayLocale = getDefaultLocale(CAT_DISPLAY);
Locale l = Locale.forLanguageTag(displayLocale.replace('_', '-'));
tmpSet.addAll(c.getCandidateLocales("", l));
nativeDisplayLanguage = l.getLanguage();
String formatLocale = getDefaultLocale(CAT_FORMAT);
if (!formatLocale.equals(displayLocale)) {
l = Locale.forLanguageTag(formatLocale.replace('_', '-'));
tmpSet.addAll(c.getCandidateLocales("", l));
}
} else {
nativeDisplayLanguage = "";
}
supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
}
private final static Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
public static DateFormatProvider getDateFormatProvider() {
return new DateFormatProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedCalendarLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedCalendarLocale(locale);
}
@Override
public DateFormat getDateInstance(int style, Locale locale) {
AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
return new SimpleDateFormat(patterns.get(style/2),
getCalendarLocale(locale));
}
@Override
public DateFormat getTimeInstance(int style, Locale locale) {
AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
return new SimpleDateFormat(patterns.get(style/2+2),
getCalendarLocale(locale));
}
@Override
public DateFormat getDateTimeInstance(int dateStyle,
int timeStyle, Locale locale) {
AtomicReferenceArray<String> patterns = getDateTimePatterns(locale);
String pattern = new StringBuilder(patterns.get(dateStyle/2))
.append(" ")
.append(patterns.get(timeStyle/2+2))
.toString();
return new SimpleDateFormat(pattern, getCalendarLocale(locale));
}
private AtomicReferenceArray<String> getDateTimePatterns(Locale locale) {
AtomicReferenceArray<String> patterns;
SoftReference<AtomicReferenceArray<String>> ref = dateFormatCache.get(locale);
if (ref == null || (patterns = ref.get()) == null) {
String langtag = removeExtensions(locale).toLanguageTag();
patterns = new AtomicReferenceArray<>(4);
patterns.compareAndSet(0, null, convertDateTimePattern(
getDateTimePattern(DateFormat.LONG, -1, langtag)));
patterns.compareAndSet(1, null, convertDateTimePattern(
getDateTimePattern(DateFormat.SHORT, -1, langtag)));
patterns.compareAndSet(2, null, convertDateTimePattern(
getDateTimePattern(-1, DateFormat.LONG, langtag)));
patterns.compareAndSet(3, null, convertDateTimePattern(
getDateTimePattern(-1, DateFormat.SHORT, langtag)));
ref = new SoftReference<>(patterns);
dateFormatCache.put(locale, ref);
}
return patterns;
}
};
}
public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
return new DateFormatSymbolsProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedCalendarLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedCalendarLocale(locale);
}
@Override
public DateFormatSymbols getInstance(Locale locale) {
DateFormatSymbols dfs;
SoftReference<DateFormatSymbols> ref =
dateFormatSymbolsCache.get(locale);
if (ref == null || (dfs = ref.get()) == null) {
dfs = new DateFormatSymbols(locale);
String langTag = removeExtensions(locale).toLanguageTag();
dfs.setAmPmStrings(getAmPmStrings(langTag, dfs.getAmPmStrings()));
dfs.setEras(getEras(langTag, dfs.getEras()));
dfs.setMonths(getMonths(langTag, dfs.getMonths()));
dfs.setShortMonths(getShortMonths(langTag, dfs.getShortMonths()));
dfs.setWeekdays(getWeekdays(langTag, dfs.getWeekdays()));
dfs.setShortWeekdays(getShortWeekdays(langTag, dfs.getShortWeekdays()));
ref = new SoftReference<>(dfs);
dateFormatSymbolsCache.put(locale, ref);
}
return (DateFormatSymbols)dfs.clone();
}
};
}
public static NumberFormatProvider getNumberFormatProvider() {
return new NumberFormatProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedNativeDigitLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedNativeDigitLocale(locale);
}
@Override
public NumberFormat getCurrencyInstance(Locale locale) {
AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
return new DecimalFormat(patterns.get(NF_CURRENCY),
DecimalFormatSymbols.getInstance(locale));
}
@Override
public NumberFormat getIntegerInstance(Locale locale) {
AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
return new DecimalFormat(patterns.get(NF_INTEGER),
DecimalFormatSymbols.getInstance(locale));
}
@Override
public NumberFormat getNumberInstance(Locale locale) {
AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
return new DecimalFormat(patterns.get(NF_NUMBER),
DecimalFormatSymbols.getInstance(locale));
}
@Override
public NumberFormat getPercentInstance(Locale locale) {
AtomicReferenceArray<String> patterns = getNumberPatterns(locale);
return new DecimalFormat(patterns.get(NF_PERCENT),
DecimalFormatSymbols.getInstance(locale));
}
private AtomicReferenceArray<String> getNumberPatterns(Locale locale) {
AtomicReferenceArray<String> patterns;
SoftReference<AtomicReferenceArray<String>> ref = numberFormatCache.get(locale);
if (ref == null || (patterns = ref.get()) == null) {
String langtag = locale.toLanguageTag();
patterns = new AtomicReferenceArray<>(NF_MAX+1);
for (int i = 0; i <= NF_MAX; i++) {
patterns.compareAndSet(i, null, getNumberPattern(i, langtag));
}
ref = new SoftReference<>(patterns);
numberFormatCache.put(locale, ref);
}
return patterns;
}
};
}
public static DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {
return new DecimalFormatSymbolsProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedNativeDigitLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedNativeDigitLocale(locale);
}
@Override
public DecimalFormatSymbols getInstance(Locale locale) {
DecimalFormatSymbols dfs;
SoftReference<DecimalFormatSymbols> ref =
decimalFormatSymbolsCache.get(locale);
if (ref == null || (dfs = ref.get()) == null) {
dfs = new DecimalFormatSymbols(getNumberLocale(locale));
String langTag = removeExtensions(locale).toLanguageTag();
// DecimalFormatSymbols.setInternationalCurrencySymbol() has
// a side effect of setting the currency symbol as well. So
// the calling order is relevant here.
dfs.setInternationalCurrencySymbol(getInternationalCurrencySymbol(langTag, dfs.getInternationalCurrencySymbol()));
dfs.setCurrencySymbol(getCurrencySymbol(langTag, dfs.getCurrencySymbol()));
dfs.setDecimalSeparator(getDecimalSeparator(langTag, dfs.getDecimalSeparator()));
dfs.setGroupingSeparator(getGroupingSeparator(langTag, dfs.getGroupingSeparator()));
dfs.setInfinity(getInfinity(langTag, dfs.getInfinity()));
dfs.setMinusSign(getMinusSign(langTag, dfs.getMinusSign()));
dfs.setMonetaryDecimalSeparator(getMonetaryDecimalSeparator(langTag, dfs.getMonetaryDecimalSeparator()));
dfs.setNaN(getNaN(langTag, dfs.getNaN()));
dfs.setPercent(getPercent(langTag, dfs.getPercent()));
dfs.setPerMill(getPerMill(langTag, dfs.getPerMill()));
dfs.setZeroDigit(getZeroDigit(langTag, dfs.getZeroDigit()));
ref = new SoftReference<>(dfs);
decimalFormatSymbolsCache.put(locale, ref);
}
return (DecimalFormatSymbols)dfs.clone();
}
};
}
public static CalendarDataProvider getCalendarDataProvider() {
return new CalendarDataProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedCalendarLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedCalendarLocale(locale);
}
@Override
public int getFirstDayOfWeek(Locale locale) {
int first = getCalendarDataValue(
removeExtensions(locale).toLanguageTag(),
CD_FIRSTDAYOFWEEK);
if (first != -1) {
return (first + 1) % 7 + 1;
} else {
return 0;
}
}
@Override
public int getMinimalDaysInFirstWeek(Locale locale) {
return 0;
}
};
}
public static CalendarProvider getCalendarProvider() {
return new CalendarProvider() {
@Override
public Locale[] getAvailableLocales() {
return getSupportedCalendarLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return isSupportedCalendarLocale(locale);
}
@Override
public Calendar getInstance(TimeZone zone, Locale locale) {
return new Calendar.Builder()
.setLocale(getCalendarLocale(locale))
.setTimeZone(zone)
.setInstant(System.currentTimeMillis())
.build();
}
};
}
public static CurrencyNameProvider getCurrencyNameProvider() {
return new CurrencyNameProvider() {
@Override
public Locale[] getAvailableLocales() {
return supportedLocale;
}
@Override
public boolean isSupportedLocale(Locale locale) {
// Ignore the extensions for now
return supportedLocaleSet.contains(locale.stripExtensions()) &&
locale.getLanguage().equals(nativeDisplayLanguage);
}
@Override
public String getSymbol(String currencyCode, Locale locale) {
// Retrieves the currency symbol by calling
// GetLocaleInfoEx(LOCALE_SCURRENCY).
// It only works with the "locale"'s currency in its native
// language.
try {
if (Currency.getInstance(locale).getCurrencyCode()
.equals(currencyCode)) {
return getDisplayString(locale.toLanguageTag(),
DN_CURRENCY_SYMBOL, currencyCode);
}
} catch (IllegalArgumentException iae) {}
return null;
}
@Override
public String getDisplayName(String currencyCode, Locale locale) {
// Retrieves the display name by calling
// GetLocaleInfoEx(LOCALE_SNATIVECURRNAME).
// It only works with the "locale"'s currency in its native
// language.
try {
if (Currency.getInstance(locale).getCurrencyCode()
.equals(currencyCode)) {
return getDisplayString(locale.toLanguageTag(),
DN_CURRENCY_NAME, currencyCode);
}
} catch (IllegalArgumentException iae) {}
return null;
}
};
}
public static LocaleNameProvider getLocaleNameProvider() {
return new LocaleNameProvider() {
@Override
public Locale[] getAvailableLocales() {
return supportedLocale;
}
@Override
public boolean isSupportedLocale(Locale locale) {
return supportedLocaleSet.contains(locale.stripExtensions()) &&
locale.getLanguage().equals(nativeDisplayLanguage);
}
@Override
public String getDisplayLanguage(String languageCode, Locale locale) {
// Retrieves the display language name by calling
// GetLocaleInfoEx(LOCALE_SLOCALIZEDLANGUAGENAME).
return getDisplayString(locale.toLanguageTag(),
DN_LOCALE_LANGUAGE, languageCode);
}
@Override
public String getDisplayCountry(String countryCode, Locale locale) {
// Retrieves the display country name by calling
// GetLocaleInfoEx(LOCALE_SLOCALIZEDCOUNTRYNAME).
return getDisplayString(locale.toLanguageTag(),
DN_LOCALE_REGION, nativeDisplayLanguage+"-"+countryCode);
}
@Override
public String getDisplayScript(String scriptCode, Locale locale) {
return null;
}
@Override
public String getDisplayVariant(String variantCode, Locale locale) {
return null;
}
};
}
private static String convertDateTimePattern(String winPattern) {
String ret = winPattern.replaceAll("dddd", "EEEE");
ret = ret.replaceAll("ddd", "EEE");
ret = ret.replaceAll("tt", "aa");
ret = ret.replaceAll("g", "GG");
return ret;
}
private static Locale[] getSupportedCalendarLocales() {
if (supportedLocale.length != 0 &&
supportedLocaleSet.contains(Locale.JAPAN) &&
isJapaneseCalendar()) {
Locale[] sup = new Locale[supportedLocale.length+1];
sup[0] = JRELocaleConstants.JA_JP_JP;
System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
return sup;
}
return supportedLocale;
}
private static boolean isSupportedCalendarLocale(Locale locale) {
Locale base = locale;
if (base.hasExtensions() || base.getVariant() != "") {
// strip off extensions and variant.
base = new Locale.Builder()
.setLocale(locale)
.clearExtensions()
.build();
}
if (!supportedLocaleSet.contains(base)) {
return false;
}
int calid = getCalendarID(base.toLanguageTag());
if (calid <= 0 || calid >= calIDToLDML.length) {
return false;
}
String requestedCalType = locale.getUnicodeLocaleType("ca");
String nativeCalType = calIDToLDML[calid]
.replaceFirst("_.*", ""); // remove locale part.
if (requestedCalType == null) {
return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
} else {
return requestedCalType.equals(nativeCalType);
}
}
private static Locale[] getSupportedNativeDigitLocales() {
if (supportedLocale.length != 0 &&
supportedLocaleSet.contains(JRELocaleConstants.TH_TH) &&
isNativeDigit("th-TH")) {
Locale[] sup = new Locale[supportedLocale.length+1];
sup[0] = JRELocaleConstants.TH_TH_TH;
System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
return sup;
}
return supportedLocale;
}
private static boolean isSupportedNativeDigitLocale(Locale locale) {
// special case for th_TH_TH
if (JRELocaleConstants.TH_TH_TH.equals(locale)) {
return isNativeDigit("th-TH");
}
String numtype = null;
Locale base = locale;
if (locale.hasExtensions()) {
numtype = locale.getUnicodeLocaleType("nu");
base = locale.stripExtensions();
}
if (supportedLocaleSet.contains(base)) {
// Only supports Latin or Thai (in thai locales) digits.
if (numtype == null || numtype.equals("latn")) {
return true;
} else if (locale.getLanguage().equals("th")) {
return "thai".equals(numtype) &&
isNativeDigit(locale.toLanguageTag());
}
}
return false;
}
private static Locale removeExtensions(Locale src) {
return new Locale.Builder().setLocale(src).clearExtensions().build();
}
private static boolean isJapaneseCalendar() {
return getCalendarID("ja-JP") == 3; // 3: CAL_JAPAN
}
private static Locale getCalendarLocale(Locale locale) {
int calid = getCalendarID(locale.toLanguageTag());
if (calid > 0 && calid < calIDToLDML.length) {
Locale.Builder lb = new Locale.Builder();
String[] caltype = calIDToLDML[calid].split("_");
if (caltype.length > 1) {
lb.setLocale(Locale.forLanguageTag(caltype[1]));
} else {
lb.setLocale(locale);
}
lb.setUnicodeLocaleKeyword("ca", caltype[0]);
return lb.build();
}
return locale;
}
private static Locale getNumberLocale(Locale src) {
if (JRELocaleConstants.TH_TH.equals(src)) {
if (isNativeDigit("th-TH")) {
Locale.Builder lb = new Locale.Builder().setLocale(src);
lb.setUnicodeLocaleKeyword("nu", "thai");
return lb.build();
}
}
return src;
}
// native methods
// initialize
private static native boolean initialize();
private static native String getDefaultLocale(int cat);
// For DateFormatProvider
private static native String getDateTimePattern(int dateStyle, int timeStyle, String langTag);
private static native int getCalendarID(String langTag);
// For DateFormatSymbolsProvider
private static native String[] getAmPmStrings(String langTag, String[] ampm);
private static native String[] getEras(String langTag, String[] eras);
private static native String[] getMonths(String langTag, String[] months);
private static native String[] getShortMonths(String langTag, String[] smonths);
private static native String[] getWeekdays(String langTag, String[] wdays);
private static native String[] getShortWeekdays(String langTag, String[] swdays);
// For NumberFormatProvider
private static native String getNumberPattern(int numberStyle, String langTag);
private static native boolean isNativeDigit(String langTag);
// For DecimalFormatSymbolsProvider
private static native String getCurrencySymbol(String langTag, String currencySymbol);
private static native char getDecimalSeparator(String langTag, char decimalSeparator);
private static native char getGroupingSeparator(String langTag, char groupingSeparator);
private static native String getInfinity(String langTag, String infinity);
private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol);
private static native char getMinusSign(String langTag, char minusSign);
private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator);
private static native String getNaN(String langTag, String nan);
private static native char getPercent(String langTag, char percent);
private static native char getPerMill(String langTag, char perMill);
private static native char getZeroDigit(String langTag, char zeroDigit);
// For CalendarDataProvider
private static native int getCalendarDataValue(String langTag, int type);
// For Locale/CurrencyNameProvider
private static native String getDisplayString(String langTag, int key, String value);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2012, 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.
*/
package sun.util.locale.provider;
import java.util.Locale;
/**
* Singletons for the well-known JRE-specific Locales. (th_TH isn't JRE specific,
* but it's treated as a special Locale because of the Thai Buddhist calendar
* support.)
*
* @author Masayoshi Okutsu
*/
public class JRELocaleConstants {
public static final Locale JA_JP_JP = new Locale("ja", "JP", "JP");
public static final Locale NO_NO_NY = new Locale("no", "NO", "NY");
public static final Locale TH_TH = new Locale("th", "TH");
public static final Locale TH_TH_TH = new Locale("th", "TH", "TH");
private JRELocaleConstants() {
}
}

View File

@@ -0,0 +1,468 @@
/*
* Copyright (c) 2012, 2015, 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.
*/
package sun.util.locale.provider;
import java.io.File;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.spi.BreakIteratorProvider;
import java.text.spi.CollatorProvider;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import java.util.spi.LocaleServiceProvider;
import java.util.spi.TimeZoneNameProvider;
import sun.util.resources.LocaleData;
import sun.util.spi.CalendarProvider;
/**
* LocaleProviderAdapter implementation for the legacy JRE locale data.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter {
private static final String LOCALE_DATA_JAR_NAME = "localedata.jar";
private final ConcurrentMap<String, Set<String>> langtagSets
= new ConcurrentHashMap<>();
private final ConcurrentMap<Locale, LocaleResources> localeResourcesMap
= new ConcurrentHashMap<>();
// LocaleData specific to this LocaleProviderAdapter.
private volatile LocaleData localeData;
/**
* Returns the type of this LocaleProviderAdapter
*/
@Override
public LocaleProviderAdapter.Type getAdapterType() {
return Type.JRE;
}
/**
* Getter method for Locale Service Providers
*/
@Override
@SuppressWarnings("unchecked")
public <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c) {
switch (c.getSimpleName()) {
case "BreakIteratorProvider":
return (P) getBreakIteratorProvider();
case "CollatorProvider":
return (P) getCollatorProvider();
case "DateFormatProvider":
return (P) getDateFormatProvider();
case "DateFormatSymbolsProvider":
return (P) getDateFormatSymbolsProvider();
case "DecimalFormatSymbolsProvider":
return (P) getDecimalFormatSymbolsProvider();
case "NumberFormatProvider":
return (P) getNumberFormatProvider();
case "CurrencyNameProvider":
return (P) getCurrencyNameProvider();
case "LocaleNameProvider":
return (P) getLocaleNameProvider();
case "TimeZoneNameProvider":
return (P) getTimeZoneNameProvider();
case "CalendarDataProvider":
return (P) getCalendarDataProvider();
case "CalendarNameProvider":
return (P) getCalendarNameProvider();
case "CalendarProvider":
return (P) getCalendarProvider();
default:
throw new InternalError("should not come down here");
}
}
private volatile BreakIteratorProvider breakIteratorProvider = null;
private volatile CollatorProvider collatorProvider = null;
private volatile DateFormatProvider dateFormatProvider = null;
private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider = null;
private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider = null;
private volatile NumberFormatProvider numberFormatProvider = null;
private volatile CurrencyNameProvider currencyNameProvider = null;
private volatile LocaleNameProvider localeNameProvider = null;
private volatile TimeZoneNameProvider timeZoneNameProvider = null;
private volatile CalendarDataProvider calendarDataProvider = null;
private volatile CalendarNameProvider calendarNameProvider = null;
private volatile CalendarProvider calendarProvider = null;
/*
* Getter methods for java.text.spi.* providers
*/
@Override
public BreakIteratorProvider getBreakIteratorProvider() {
if (breakIteratorProvider == null) {
BreakIteratorProvider provider = new BreakIteratorProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (breakIteratorProvider == null) {
breakIteratorProvider = provider;
}
}
}
return breakIteratorProvider;
}
@Override
public CollatorProvider getCollatorProvider() {
if (collatorProvider == null) {
CollatorProvider provider = new CollatorProviderImpl(getAdapterType(),
getLanguageTagSet("CollationData"));
synchronized (this) {
if (collatorProvider == null) {
collatorProvider = provider;
}
}
}
return collatorProvider;
}
@Override
public DateFormatProvider getDateFormatProvider() {
if (dateFormatProvider == null) {
DateFormatProvider provider = new DateFormatProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (dateFormatProvider == null) {
dateFormatProvider = provider;
}
}
}
return dateFormatProvider;
}
@Override
public DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
if (dateFormatSymbolsProvider == null) {
DateFormatSymbolsProvider provider = new DateFormatSymbolsProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (dateFormatSymbolsProvider == null) {
dateFormatSymbolsProvider = provider;
}
}
}
return dateFormatSymbolsProvider;
}
@Override
public DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {
if (decimalFormatSymbolsProvider == null) {
DecimalFormatSymbolsProvider provider = new DecimalFormatSymbolsProviderImpl(getAdapterType(), getLanguageTagSet("FormatData"));
synchronized (this) {
if (decimalFormatSymbolsProvider == null) {
decimalFormatSymbolsProvider = provider;
}
}
}
return decimalFormatSymbolsProvider;
}
@Override
public NumberFormatProvider getNumberFormatProvider() {
if (numberFormatProvider == null) {
NumberFormatProvider provider = new NumberFormatProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (numberFormatProvider == null) {
numberFormatProvider = provider;
}
}
}
return numberFormatProvider;
}
/**
* Getter methods for java.util.spi.* providers
*/
@Override
public CurrencyNameProvider getCurrencyNameProvider() {
if (currencyNameProvider == null) {
CurrencyNameProvider provider = new CurrencyNameProviderImpl(getAdapterType(),
getLanguageTagSet("CurrencyNames"));
synchronized (this) {
if (currencyNameProvider == null) {
currencyNameProvider = provider;
}
}
}
return currencyNameProvider;
}
@Override
public LocaleNameProvider getLocaleNameProvider() {
if (localeNameProvider == null) {
LocaleNameProvider provider = new LocaleNameProviderImpl(getAdapterType(),
getLanguageTagSet("LocaleNames"));
synchronized (this) {
if (localeNameProvider == null) {
localeNameProvider = provider;
}
}
}
return localeNameProvider;
}
@Override
public TimeZoneNameProvider getTimeZoneNameProvider() {
if (timeZoneNameProvider == null) {
TimeZoneNameProvider provider = new TimeZoneNameProviderImpl(getAdapterType(),
getLanguageTagSet("TimeZoneNames"));
synchronized (this) {
if (timeZoneNameProvider == null) {
timeZoneNameProvider = provider;
}
}
}
return timeZoneNameProvider;
}
@Override
public CalendarDataProvider getCalendarDataProvider() {
if (calendarDataProvider == null) {
CalendarDataProvider provider;
provider = new CalendarDataProviderImpl(getAdapterType(),
getLanguageTagSet("CalendarData"));
synchronized (this) {
if (calendarDataProvider == null) {
calendarDataProvider = provider;
}
}
}
return calendarDataProvider;
}
@Override
public CalendarNameProvider getCalendarNameProvider() {
if (calendarNameProvider == null) {
CalendarNameProvider provider;
provider = new CalendarNameProviderImpl(getAdapterType(),
getLanguageTagSet("FormatData"));
synchronized (this) {
if (calendarNameProvider == null) {
calendarNameProvider = provider;
}
}
}
return calendarNameProvider;
}
/**
* Getter methods for sun.util.spi.* providers
*/
@Override
public CalendarProvider getCalendarProvider() {
if (calendarProvider == null) {
CalendarProvider provider = new CalendarProviderImpl(getAdapterType(),
getLanguageTagSet("CalendarData"));
synchronized (this) {
if (calendarProvider == null) {
calendarProvider = provider;
}
}
}
return calendarProvider;
}
@Override
public LocaleResources getLocaleResources(Locale locale) {
LocaleResources lr = localeResourcesMap.get(locale);
if (lr == null) {
lr = new LocaleResources(this, locale);
LocaleResources lrc = localeResourcesMap.putIfAbsent(locale, lr);
if (lrc != null) {
lr = lrc;
}
}
return lr;
}
// ResourceBundleBasedAdapter method implementation
@Override
public LocaleData getLocaleData() {
if (localeData == null) {
synchronized (this) {
if (localeData == null) {
localeData = new LocaleData(getAdapterType());
}
}
}
return localeData;
}
/**
* Returns a list of the installed locales. Currently, this simply returns
* the list of locales for which a sun.text.resources.FormatData bundle
* exists. This bundle family happens to be the one with the broadest
* locale coverage in the JRE.
*/
@Override
public Locale[] getAvailableLocales() {
return AvailableJRELocales.localeList.clone();
}
public Set<String> getLanguageTagSet(String category) {
Set<String> tagset = langtagSets.get(category);
if (tagset == null) {
tagset = createLanguageTagSet(category);
Set<String> ts = langtagSets.putIfAbsent(category, tagset);
if (ts != null) {
tagset = ts;
}
}
return tagset;
}
protected Set<String> createLanguageTagSet(String category) {
String supportedLocaleString = LocaleDataMetaInfo.getSupportedLocaleString(category);
if (supportedLocaleString == null) {
return Collections.emptySet();
}
Set<String> tagset = new HashSet<>();
StringTokenizer tokens = new StringTokenizer(supportedLocaleString);
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
if (token.equals("|")) {
if (isNonENLangSupported()) {
continue;
}
break;
}
tagset.add(token);
}
return tagset;
}
/**
* Lazy load available locales.
*/
private static class AvailableJRELocales {
private static final Locale[] localeList = createAvailableLocales();
private AvailableJRELocales() {
}
}
private static Locale[] createAvailableLocales() {
/*
* Gets the locale string list from LocaleDataMetaInfo class and then
* contructs the Locale array and a set of language tags based on the
* locale string returned above.
*/
String supportedLocaleString = LocaleDataMetaInfo.getSupportedLocaleString("AvailableLocales");
if (supportedLocaleString.length() == 0) {
throw new InternalError("No available locales for JRE");
}
/*
* Look for "|" and construct a new locale string list.
*/
int barIndex = supportedLocaleString.indexOf('|');
StringTokenizer localeStringTokenizer;
if (isNonENLangSupported()) {
localeStringTokenizer = new StringTokenizer(supportedLocaleString.substring(0, barIndex)
+ supportedLocaleString.substring(barIndex + 1));
} else {
localeStringTokenizer = new StringTokenizer(supportedLocaleString.substring(0, barIndex));
}
int length = localeStringTokenizer.countTokens();
Locale[] locales = new Locale[length + 1];
locales[0] = Locale.ROOT;
for (int i = 1; i <= length; i++) {
String currentToken = localeStringTokenizer.nextToken();
switch (currentToken) {
case "ja-JP-JP":
locales[i] = JRELocaleConstants.JA_JP_JP;
break;
case "no-NO-NY":
locales[i] = JRELocaleConstants.NO_NO_NY;
break;
case "th-TH-TH":
locales[i] = JRELocaleConstants.TH_TH_TH;
break;
default:
locales[i] = Locale.forLanguageTag(currentToken);
}
}
return locales;
}
private static volatile Boolean isNonENSupported = null;
/*
* Returns true if the non EN resources jar file exists in jre
* extension directory. @returns true if the jar file is there. Otherwise,
* returns false.
*/
private static boolean isNonENLangSupported() {
if (isNonENSupported == null) {
synchronized (JRELocaleProviderAdapter.class) {
if (isNonENSupported == null) {
final String sep = File.separator;
String localeDataJar =
java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("java.home"))
+ sep + "lib" + sep + "ext" + sep + LOCALE_DATA_JAR_NAME;
/*
* Peek at the installed extension directory to see if
* localedata.jar is installed or not.
*/
final File f = new File(localeDataJar);
isNonENSupported =
AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return f.exists();
}
});
}
}
}
return isNonENSupported;
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2005, 2015, 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.
*/
// -- This file was mechanically generated: Do not edit! -- //
/*
* This class contains a map which records the locale list string for
* each resource in sun.util.resources & sun.text.resources.
* It is used to avoid loading non-existent localized resources so that
* jar files won't be opened unnecessary to look up them.
*
* @since 1.6
*/
package sun.util.locale.provider;
import java.util.HashMap;
public class LocaleDataMetaInfo {
private static final HashMap<String, String> resourceNameToLocales =
new HashMap<String, String>(7);
static {
/* During JDK build time, #XXX_YYY# will be replaced by a string contain all the locales
supported by the resource.
Don't remove the space character between " and #. That is put there purposely so that
look up locale string such as "en" could be based on if it contains " en ".
*/
resourceNameToLocales.put("FormatData",
" en en-AU en-CA en-GB en-IE en-IN en-MT en-NZ en-PH en-SG en-US en-ZA | ar ar-JO ar-LB ar-SY be be-BY bg bg-BG ca ca-ES cs cs-CZ da da-DK de de-AT de-CH de-DE de-LU el el-CY el-GR es es-AR es-BO es-CL es-CO es-CR es-DO es-EC es-ES es-GT es-HN es-MX es-NI es-PA es-PE es-PR es-PY es-SV es-US es-UY es-VE et et-EE fi fi-FI fr fr-BE fr-CA fr-CH fr-FR ga ga-IE hi-IN hr hr-HR hu hu-HU in in-ID is is-IS it it-CH it-IT iw iw-IL ja ja-JP ko ko-KR lt lt-LT lv lv-LV mk mk-MK ms ms-MY mt mt-MT nl nl-BE nl-NL no no-NO no-NO-NY pl pl-PL pt pt-BR pt-PT ro ro-RO ru ru-RU sk sk-SK sl sl-SI sq sq-AL sr sr-BA sr-CS sr-Latn sr-Latn-ME sr-ME sr-RS sv sv-SE th th-TH tr tr-TR uk uk-UA vi vi-VN zh zh-CN zh-HK zh-SG zh-TW ");
resourceNameToLocales.put("CollationData",
" | ar be bg ca cs da el es et fi fr hi hr hu is iw ja ko lt lv mk no pl ro ru sk sl sq sr sr-Latn sv th tr uk vi zh zh-HK zh-TW ");
resourceNameToLocales.put("BreakIteratorInfo",
" | th ");
resourceNameToLocales.put("BreakIteratorRules",
" | th ");
resourceNameToLocales.put("TimeZoneNames",
" en en-CA en-GB en-IE | de es fr hi it ja ko pt-BR sv zh-CN zh-HK zh-TW ");
resourceNameToLocales.put("LocaleNames",
" en en-MT en-PH en-SG | ar be bg ca cs da de el el-CY es es-US et fi fr ga hi hr hu in is it iw ja ko lt lv mk ms mt nl no no-NO-NY pl pt pt-PT ro ru sk sl sq sr sr-Latn sv th tr uk vi zh zh-HK zh-SG zh-TW ");
resourceNameToLocales.put("CurrencyNames",
" en-AU en-CA en-GB en-IE en-IN en-MT en-NZ en-PH en-SG en-US en-ZA | ar-AE ar-BH ar-DZ ar-EG ar-IQ ar-JO ar-KW ar-LB ar-LY ar-MA ar-OM ar-QA ar-SA ar-SD ar-SY ar-TN ar-YE be-BY bg-BG ca-ES cs-CZ da-DK de de-AT de-CH de-DE de-GR de-LU el-CY el-GR es es-AR es-BO es-CL es-CO es-CR es-CU es-DO es-EC es-ES es-GT es-HN es-MX es-NI es-PA es-PE es-PR es-PY es-SV es-US es-UY es-VE et-EE fi-FI fr fr-BE fr-CA fr-CH fr-FR fr-LU ga-IE hi-IN hr-HR hu-HU in-ID is-IS it it-CH it-IT iw-IL ja ja-JP ko ko-KR lt-LT lv-LV mk-MK ms-MY mt-MT nl-BE nl-NL no-NO pl-PL pt pt-BR pt-PT ro-RO ru-RU sk-SK sl-SI sq-AL sr-BA sr-CS sr-Latn-BA sr-Latn-ME sr-Latn-RS sr-ME sr-RS sv sv-SE th-TH tr-TR uk-UA vi-VN zh-CN zh-HK zh-SG zh-TW ");
resourceNameToLocales.put("CalendarData",
" en en-GB en-IE en-MT | ar be bg ca cs da de el el-CY es es-ES es-US et fi fr fr-CA hi hr hu in-ID is it iw ja ko lt lv mk ms-MY mt mt-MT nl no pl pt pt-BR pt-PT ro ru sk sl sq sr sr-Latn-BA sr-Latn-ME sr-Latn-RS sv th tr uk vi zh ");
resourceNameToLocales.put("AvailableLocales",
" en en-AU en-CA en-GB en-IE en-IN en-MT en-NZ en-PH en-SG en-US en-ZA | ar ar-AE ar-BH ar-DZ ar-EG ar-IQ ar-JO ar-KW ar-LB ar-LY ar-MA ar-OM ar-QA ar-SA ar-SD ar-SY ar-TN ar-YE be be-BY bg bg-BG ca ca-ES cs cs-CZ da da-DK de de-AT de-CH de-DE de-GR de-LU el el-CY el-GR es es-AR es-BO es-CL es-CO es-CR es-CU es-DO es-EC es-ES es-GT es-HN es-MX es-NI es-PA es-PE es-PR es-PY es-SV es-US es-UY es-VE et et-EE fi fi-FI fr fr-BE fr-CA fr-CH fr-FR fr-LU ga ga-IE hi hi-IN hr hr-HR hu hu-HU in in-ID is is-IS it it-CH it-IT iw iw-IL ja ja-JP ja-JP-JP ko ko-KR lt lt-LT lv lv-LV mk mk-MK ms ms-MY mt mt-MT nl nl-BE nl-NL no no-NO no-NO-NY pl pl-PL pt pt-BR pt-PT ro ro-RO ru ru-RU sk sk-SK sl sl-SI sq sq-AL sr sr-BA sr-CS sr-Latn sr-Latn-BA sr-Latn-ME sr-Latn-RS sr-ME sr-RS sv sv-SE th th-TH th-TH-TH tr tr-TR uk uk-UA vi vi-VN zh zh-CN zh-HK zh-SG zh-TW ");
}
/*
* @param resourceName the resource name
* @return the supported locale string for the passed in resource.
*/
public static String getSupportedLocaleString(String resourceName) {
return resourceNameToLocales.get(resourceName);
}
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
package sun.util.locale.provider;
import java.util.Locale;
import java.util.Set;
import java.util.spi.LocaleNameProvider;
/**
* Concrete implementation of the
* {@link java.util.spi.LocaleNameProvider LocaleNameProvider} class
* for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class LocaleNameProviderImpl extends LocaleNameProvider implements AvailableLanguageTags {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public LocaleNameProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a localized name for the given ISO 639 language code and the
* given locale that is appropriate for display to the user.
* For example, if <code>languageCode</code> is "fr" and <code>locale</code>
* is en_US, getDisplayLanguage() will return "French"; if <code>languageCode</code>
* is "en" and <code>locale</code> is fr_FR, getDisplayLanguage() will return "anglais".
* If the name returned cannot be localized according to <code>locale</code>,
* (say, the provider does not have a Japanese name for Croatian),
* this method returns null.
* @param languageCode the ISO 639 language code string in the form of two
* lower-case letters between 'a' (U+0061) and 'z' (U+007A)
* @param locale the desired locale
* @return the name of the given language code for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>languageCode</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>languageCode</code> is not in the form of
* two lower-case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Locale#getDisplayLanguage(java.util.Locale)
*/
@Override
public String getDisplayLanguage(String lang, Locale locale) {
return getDisplayString(lang, locale);
}
/**
* Returns a localized name for the given <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
* IETF BCP47</a> script code and the given locale that is appropriate for
* display to the user.
* For example, if <code>scriptCode</code> is "Latn" and <code>locale</code>
* is en_US, getDisplayScript() will return "Latin"; if <code>scriptCode</code>
* is "Cyrl" and <code>locale</code> is fr_FR, getDisplayScript() will return "cyrillique".
* If the name returned cannot be localized according to <code>locale</code>,
* (say, the provider does not have a Japanese name for Cyrillic),
* this method returns null. The default implementation returns null.
* @param scriptCode the four letter script code string in the form of title-case
* letters (the first letter is upper-case character between 'A' (U+0041) and
* 'Z' (U+005A) followed by three lower-case character between 'a' (U+0061)
* and 'z' (U+007A)).
* @param locale the desired locale
* @return the name of the given script code for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>scriptCode</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>scriptCode</code> is not in the form of
* four title case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Locale#getDisplayScript(java.util.Locale)
*/
@Override
public String getDisplayScript(String scriptCode, Locale locale) {
return getDisplayString(scriptCode, locale);
}
/**
* Returns a localized name for the given ISO 3166 country code and the
* given locale that is appropriate for display to the user.
* For example, if <code>countryCode</code> is "FR" and <code>locale</code>
* is en_US, getDisplayCountry() will return "France"; if <code>countryCode</code>
* is "US" and <code>locale</code> is fr_FR, getDisplayCountry() will return "Etats-Unis".
* If the name returned cannot be localized according to <code>locale</code>,
* (say, the provider does not have a Japanese name for Croatia),
* this method returns null.
* @param countryCode the ISO 3166 country code string in the form of two
* upper-case letters between 'A' (U+0041) and 'Z' (U+005A)
* @param locale the desired locale
* @return the name of the given country code for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>countryCode</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>countryCode</code> is not in the form of
* two upper-case letters, or <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Locale#getDisplayCountry(java.util.Locale)
*/
@Override
public String getDisplayCountry(String ctry, Locale locale) {
return getDisplayString(ctry, locale);
}
/**
* Returns a localized name for the given variant code and the given locale that
* is appropriate for display to the user.
* If the name returned cannot be localized according to <code>locale</code>,
* this method returns null.
* @param variant the variant string
* @param locale the desired locale
* @return the name of the given variant string for the specified locale, or null if it's not
* available.
* @exception NullPointerException if <code>variant</code> or <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @see java.util.Locale#getDisplayVariant(java.util.Locale)
*/
@Override
public String getDisplayVariant(String vrnt, Locale locale) {
return getDisplayString("%%"+vrnt, locale);
}
private String getDisplayString(String key, Locale locale) {
if (key == null || locale == null) {
throw new NullPointerException();
}
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getLocaleName(key);
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,456 @@
/*
* Copyright (c) 2012, 2015, 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.
*/
package sun.util.locale.provider;
import java.security.AccessController;
import java.text.spi.BreakIteratorProvider;
import java.text.spi.CollatorProvider;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import java.util.spi.LocaleServiceProvider;
import java.util.spi.TimeZoneNameProvider;
import sun.util.cldr.CLDRLocaleProviderAdapter;
import sun.util.spi.CalendarProvider;
/**
* The LocaleProviderAdapter abstract class.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public abstract class LocaleProviderAdapter {
/**
* Adapter type.
*/
public static enum Type {
JRE("sun.util.resources", "sun.text.resources"),
CLDR("sun.util.resources.cldr", "sun.text.resources.cldr"),
SPI,
HOST,
FALLBACK("sun.util.resources", "sun.text.resources");
private final String UTIL_RESOURCES_PACKAGE;
private final String TEXT_RESOURCES_PACKAGE;
private Type() {
this(null, null);
}
private Type(String util, String text) {
UTIL_RESOURCES_PACKAGE = util;
TEXT_RESOURCES_PACKAGE = text;
}
public String getUtilResourcesPackage() {
return UTIL_RESOURCES_PACKAGE;
}
public String getTextResourcesPackage() {
return TEXT_RESOURCES_PACKAGE;
}
}
/**
* LocaleProviderAdapter preference list. The default list is intended
* to behave the same manner in JDK7.
*/
private static final List<Type> adapterPreference;
/**
* JRE Locale Data Adapter instance
*/
private static LocaleProviderAdapter jreLocaleProviderAdapter = new JRELocaleProviderAdapter();
/**
* SPI Locale Data Adapter instance
*/
private static LocaleProviderAdapter spiLocaleProviderAdapter = new SPILocaleProviderAdapter();
/**
* CLDR Locale Data Adapter instance, if any.
*/
private static LocaleProviderAdapter cldrLocaleProviderAdapter = null;
/**
* HOST Locale Data Adapter instance, if any.
*/
private static LocaleProviderAdapter hostLocaleProviderAdapter = null;
/**
* FALLBACK Locale Data Adapter instance. It's basically the same with JRE, but only kicks
* in for the root locale.
*/
private static LocaleProviderAdapter fallbackLocaleProviderAdapter = null;
/**
* Default fallback adapter type, which should return something meaningful in any case.
* This is either JRE or FALLBACK.
*/
static LocaleProviderAdapter.Type defaultLocaleProviderAdapter = null;
/**
* Adapter lookup cache.
*/
private static ConcurrentMap<Class<? extends LocaleServiceProvider>, ConcurrentMap<Locale, LocaleProviderAdapter>>
adapterCache = new ConcurrentHashMap<>();
static {
String order = AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("java.locale.providers"));
List<Type> typeList = new ArrayList<>();
// Check user specified adapter preference
if (order != null && order.length() != 0) {
String[] types = order.split(",");
for (String type : types) {
try {
Type aType = Type.valueOf(type.trim().toUpperCase(Locale.ROOT));
// load adapter if necessary
switch (aType) {
case CLDR:
if (cldrLocaleProviderAdapter == null) {
cldrLocaleProviderAdapter = new CLDRLocaleProviderAdapter();
}
break;
case HOST:
if (hostLocaleProviderAdapter == null) {
hostLocaleProviderAdapter = new HostLocaleProviderAdapter();
}
break;
}
if (!typeList.contains(aType)) {
typeList.add(aType);
}
} catch (IllegalArgumentException | UnsupportedOperationException e) {
// could be caused by the user specifying wrong
// provider name or format in the system property
LocaleServiceProviderPool.config(LocaleProviderAdapter.class, e.toString());
}
}
}
if (!typeList.isEmpty()) {
if (!typeList.contains(Type.JRE)) {
// Append FALLBACK as the last resort.
fallbackLocaleProviderAdapter = new FallbackLocaleProviderAdapter();
typeList.add(Type.FALLBACK);
defaultLocaleProviderAdapter = Type.FALLBACK;
} else {
defaultLocaleProviderAdapter = Type.JRE;
}
} else {
// Default preference list
typeList.add(Type.JRE);
typeList.add(Type.SPI);
defaultLocaleProviderAdapter = Type.JRE;
}
adapterPreference = Collections.unmodifiableList(typeList);
}
/**
* Returns the singleton instance for each adapter type
*/
public static LocaleProviderAdapter forType(Type type) {
switch (type) {
case JRE:
return jreLocaleProviderAdapter;
case CLDR:
return cldrLocaleProviderAdapter;
case SPI:
return spiLocaleProviderAdapter;
case HOST:
return hostLocaleProviderAdapter;
case FALLBACK:
return fallbackLocaleProviderAdapter;
default:
throw new InternalError("unknown locale data adapter type");
}
}
public static LocaleProviderAdapter forJRE() {
return jreLocaleProviderAdapter;
}
public static LocaleProviderAdapter getResourceBundleBased() {
for (Type type : getAdapterPreference()) {
if (type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK) {
return forType(type);
}
}
// Shouldn't happen.
throw new InternalError();
}
/**
* Returns the preference order of LocaleProviderAdapter.Type
*/
public static List<Type> getAdapterPreference() {
return adapterPreference;
}
/**
* Returns a LocaleProviderAdapter for the given locale service provider that
* best matches the given locale. This method returns the LocaleProviderAdapter
* for JRE if none is found for the given locale.
*
* @param providerClass the class for the locale service provider
* @param locale the desired locale.
* @return a LocaleProviderAdapter
*/
public static LocaleProviderAdapter getAdapter(Class<? extends LocaleServiceProvider> providerClass,
Locale locale) {
LocaleProviderAdapter adapter;
// cache lookup
ConcurrentMap<Locale, LocaleProviderAdapter> adapterMap = adapterCache.get(providerClass);
if (adapterMap != null) {
if ((adapter = adapterMap.get(locale)) != null) {
return adapter;
}
} else {
adapterMap = new ConcurrentHashMap<>();
adapterCache.putIfAbsent(providerClass, adapterMap);
}
// Fast look-up for the given locale
adapter = findAdapter(providerClass, locale);
if (adapter != null) {
adapterMap.putIfAbsent(locale, adapter);
return adapter;
}
// Try finding an adapter in the normal candidate locales path of the given locale.
List<Locale> lookupLocales = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT)
.getCandidateLocales("", locale);
for (Locale loc : lookupLocales) {
if (loc.equals(locale)) {
// We've already done with this loc.
continue;
}
adapter = findAdapter(providerClass, loc);
if (adapter != null) {
adapterMap.putIfAbsent(locale, adapter);
return adapter;
}
}
// returns the adapter for FALLBACK as the last resort
adapterMap.putIfAbsent(locale, fallbackLocaleProviderAdapter);
return fallbackLocaleProviderAdapter;
}
private static LocaleProviderAdapter findAdapter(Class<? extends LocaleServiceProvider> providerClass,
Locale locale) {
for (Type type : getAdapterPreference()) {
LocaleProviderAdapter adapter = forType(type);
LocaleServiceProvider provider = adapter.getLocaleServiceProvider(providerClass);
if (provider != null) {
if (provider.isSupportedLocale(locale)) {
return adapter;
}
}
}
return null;
}
/**
* A utility method for implementing the default LocaleServiceProvider.isSupportedLocale
* for the JRE, CLDR, and FALLBACK adapters.
*/
public static boolean isSupportedLocale(Locale locale, LocaleProviderAdapter.Type type, Set<String> langtags) {
assert type == Type.JRE || type == Type.CLDR || type == Type.FALLBACK;
if (Locale.ROOT.equals(locale)) {
return true;
}
if (type == Type.FALLBACK) {
// no other locales except ROOT are supported for FALLBACK
return false;
}
locale = locale.stripExtensions();
if (langtags.contains(locale.toLanguageTag())) {
return true;
}
if (type == Type.JRE) {
String oldname = locale.toString().replace('_', '-');
return langtags.contains(oldname) ||
"ja-JP-JP".equals(oldname) ||
"th-TH-TH".equals(oldname) ||
"no-NO-NY".equals(oldname);
}
return false;
}
public static Locale[] toLocaleArray(Set<String> tags) {
Locale[] locs = new Locale[tags.size() + 1];
int index = 0;
locs[index++] = Locale.ROOT;
for (String tag : tags) {
switch (tag) {
case "ja-JP-JP":
locs[index++] = JRELocaleConstants.JA_JP_JP;
break;
case "th-TH-TH":
locs[index++] = JRELocaleConstants.TH_TH_TH;
break;
default:
locs[index++] = Locale.forLanguageTag(tag);
break;
}
}
return locs;
}
/**
* Returns the type of this LocaleProviderAdapter
*/
public abstract LocaleProviderAdapter.Type getAdapterType();
/**
* Getter method for Locale Service Providers.
*/
public abstract <P extends LocaleServiceProvider> P getLocaleServiceProvider(Class<P> c);
/**
* Returns a BreakIteratorProvider for this LocaleProviderAdapter, or null if no
* BreakIteratorProvider is available.
*
* @return a BreakIteratorProvider
*/
public abstract BreakIteratorProvider getBreakIteratorProvider();
/**
* Returns a ollatorProvider for this LocaleProviderAdapter, or null if no
* ollatorProvider is available.
*
* @return a ollatorProvider
*/
public abstract CollatorProvider getCollatorProvider();
/**
* Returns a DateFormatProvider for this LocaleProviderAdapter, or null if no
* DateFormatProvider is available.
*
* @return a DateFormatProvider
*/
public abstract DateFormatProvider getDateFormatProvider();
/**
* Returns a DateFormatSymbolsProvider for this LocaleProviderAdapter, or null if no
* DateFormatSymbolsProvider is available.
*
* @return a DateFormatSymbolsProvider
*/
public abstract DateFormatSymbolsProvider getDateFormatSymbolsProvider();
/**
* Returns a DecimalFormatSymbolsProvider for this LocaleProviderAdapter, or null if no
* DecimalFormatSymbolsProvider is available.
*
* @return a DecimalFormatSymbolsProvider
*/
public abstract DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider();
/**
* Returns a NumberFormatProvider for this LocaleProviderAdapter, or null if no
* NumberFormatProvider is available.
*
* @return a NumberFormatProvider
*/
public abstract NumberFormatProvider getNumberFormatProvider();
/*
* Getter methods for java.util.spi.* providers
*/
/**
* Returns a CurrencyNameProvider for this LocaleProviderAdapter, or null if no
* CurrencyNameProvider is available.
*
* @return a CurrencyNameProvider
*/
public abstract CurrencyNameProvider getCurrencyNameProvider();
/**
* Returns a LocaleNameProvider for this LocaleProviderAdapter, or null if no
* LocaleNameProvider is available.
*
* @return a LocaleNameProvider
*/
public abstract LocaleNameProvider getLocaleNameProvider();
/**
* Returns a TimeZoneNameProvider for this LocaleProviderAdapter, or null if no
* TimeZoneNameProvider is available.
*
* @return a TimeZoneNameProvider
*/
public abstract TimeZoneNameProvider getTimeZoneNameProvider();
/**
* Returns a CalendarDataProvider for this LocaleProviderAdapter, or null if no
* CalendarDataProvider is available.
*
* @return a CalendarDataProvider
*/
public abstract CalendarDataProvider getCalendarDataProvider();
/**
* Returns a CalendarNameProvider for this LocaleProviderAdapter, or null if no
* CalendarNameProvider is available.
*
* @return a CalendarNameProvider
*/
public abstract CalendarNameProvider getCalendarNameProvider();
/**
* Returns a CalendarProvider for this LocaleProviderAdapter, or null if no
* CalendarProvider is available.
*
* @return a CalendarProvider
*/
public abstract CalendarProvider getCalendarProvider();
public abstract LocaleResources getLocaleResources(Locale locale);
public abstract Locale[] getAvailableLocales();
}

View File

@@ -0,0 +1,510 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
/*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package sun.util.locale.provider;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.text.MessageFormat;
import java.util.Calendar;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import sun.util.calendar.ZoneInfo;
import sun.util.resources.LocaleData;
import sun.util.resources.OpenListResourceBundle;
import sun.util.resources.ParallelListResourceBundle;
import sun.util.resources.TimeZoneNamesBundle;
/**
* Central accessor to locale-dependent resources for JRE/CLDR provider adapters.
*
* @author Masayoshi Okutsu
* @author Naoto Sato
*/
public class LocaleResources {
private final Locale locale;
private final LocaleData localeData;
private final LocaleProviderAdapter.Type type;
// Resource cache
private ConcurrentMap<String, ResourceReference> cache = new ConcurrentHashMap<>();
private ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// cache key prefixes
private static final String BREAK_ITERATOR_INFO = "BII.";
private static final String CALENDAR_DATA = "CALD.";
private static final String COLLATION_DATA_CACHEKEY = "COLD";
private static final String DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY = "DFSD";
private static final String CURRENCY_NAMES = "CN.";
private static final String LOCALE_NAMES = "LN.";
private static final String TIME_ZONE_NAMES = "TZN.";
private static final String ZONE_IDS_CACHEKEY = "ZID";
private static final String CALENDAR_NAMES = "CALN.";
private static final String NUMBER_PATTERNS_CACHEKEY = "NP";
private static final String DATE_TIME_PATTERN = "DTP.";
// null singleton cache value
private static final Object NULLOBJECT = new Object();
LocaleResources(ResourceBundleBasedAdapter adapter, Locale locale) {
this.locale = locale;
this.localeData = adapter.getLocaleData();
type = ((LocaleProviderAdapter)adapter).getAdapterType();
}
private void removeEmptyReferences() {
Object ref;
while ((ref = referenceQueue.poll()) != null) {
cache.remove(((ResourceReference)ref).getCacheKey());
}
}
Object getBreakIteratorInfo(String key) {
Object biInfo;
String cacheKey = BREAK_ITERATOR_INFO + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data == null || ((biInfo = data.get()) == null)) {
biInfo = localeData.getBreakIteratorInfo(locale).getObject(key);
cache.put(cacheKey, new ResourceReference(cacheKey, biInfo, referenceQueue));
}
return biInfo;
}
int getCalendarData(String key) {
Integer caldata;
String cacheKey = CALENDAR_DATA + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data == null || ((caldata = (Integer) data.get()) == null)) {
ResourceBundle rb = localeData.getCalendarData(locale);
if (rb.containsKey(key)) {
caldata = Integer.parseInt(rb.getString(key));
} else {
caldata = 0;
}
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) caldata, referenceQueue));
}
return caldata;
}
public String getCollationData() {
String key = "Rule";
String coldata = "";
removeEmptyReferences();
ResourceReference data = cache.get(COLLATION_DATA_CACHEKEY);
if (data == null || ((coldata = (String) data.get()) == null)) {
ResourceBundle rb = localeData.getCollationData(locale);
if (rb.containsKey(key)) {
coldata = rb.getString(key);
}
cache.put(COLLATION_DATA_CACHEKEY,
new ResourceReference(COLLATION_DATA_CACHEKEY, (Object) coldata, referenceQueue));
}
return coldata;
}
public Object[] getDecimalFormatSymbolsData() {
Object[] dfsdata;
removeEmptyReferences();
ResourceReference data = cache.get(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY);
if (data == null || ((dfsdata = (Object[]) data.get()) == null)) {
// Note that only dfsdata[0] is prepared here in this method. Other
// elements are provided by the caller, yet they are cached here.
ResourceBundle rb = localeData.getNumberFormatData(locale);
dfsdata = new Object[3];
// NumberElements look up. First, try the Unicode extension
String numElemKey;
String numberType = locale.getUnicodeLocaleType("nu");
if (numberType != null) {
numElemKey = numberType + ".NumberElements";
if (rb.containsKey(numElemKey)) {
dfsdata[0] = rb.getStringArray(numElemKey);
}
}
// Next, try DefaultNumberingSystem value
if (dfsdata[0] == null && rb.containsKey("DefaultNumberingSystem")) {
numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements";
if (rb.containsKey(numElemKey)) {
dfsdata[0] = rb.getStringArray(numElemKey);
}
}
// Last resort. No need to check the availability.
// Just let it throw MissingResourceException when needed.
if (dfsdata[0] == null) {
dfsdata[0] = rb.getStringArray("NumberElements");
}
cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY,
new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, (Object) dfsdata, referenceQueue));
}
return dfsdata;
}
public String getCurrencyName(String key) {
Object currencyName = null;
String cacheKey = CURRENCY_NAMES + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data != null && ((currencyName = data.get()) != null)) {
if (currencyName.equals(NULLOBJECT)) {
currencyName = null;
}
return (String) currencyName;
}
OpenListResourceBundle olrb = localeData.getCurrencyNames(locale);
if (olrb.containsKey(key)) {
currencyName = olrb.getObject(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, currencyName, referenceQueue));
}
return (String) currencyName;
}
public String getLocaleName(String key) {
Object localeName = null;
String cacheKey = LOCALE_NAMES + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data != null && ((localeName = data.get()) != null)) {
if (localeName.equals(NULLOBJECT)) {
localeName = null;
}
return (String) localeName;
}
OpenListResourceBundle olrb = localeData.getLocaleNames(locale);
if (olrb.containsKey(key)) {
localeName = olrb.getObject(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, localeName, referenceQueue));
}
return (String) localeName;
}
String[] getTimeZoneNames(String key) {
String[] names = null;
String cacheKey = TIME_ZONE_NAMES + '.' + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (Objects.isNull(data) || Objects.isNull((names = (String[]) data.get()))) {
TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
if (tznb.containsKey(key)) {
names = tznb.getStringArray(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) names, referenceQueue));
}
}
return names;
}
@SuppressWarnings("unchecked")
Set<String> getZoneIDs() {
Set<String> zoneIDs = null;
removeEmptyReferences();
ResourceReference data = cache.get(ZONE_IDS_CACHEKEY);
if (data == null || ((zoneIDs = (Set<String>) data.get()) == null)) {
TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
zoneIDs = rb.keySet();
cache.put(ZONE_IDS_CACHEKEY,
new ResourceReference(ZONE_IDS_CACHEKEY, (Object) zoneIDs, referenceQueue));
}
return zoneIDs;
}
// zoneStrings are cached separately in TimeZoneNameUtility.
String[][] getZoneStrings() {
TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale);
Set<String> keyset = getZoneIDs();
// Use a LinkedHashSet to preseve the order
Set<String[]> value = new LinkedHashSet<>();
for (String key : keyset) {
value.add(rb.getStringArray(key));
}
// Add aliases data for CLDR
if (type == LocaleProviderAdapter.Type.CLDR) {
// Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
Map<String, String> aliases = ZoneInfo.getAliasTable();
for (String alias : aliases.keySet()) {
if (!keyset.contains(alias)) {
String tzid = aliases.get(alias);
if (keyset.contains(tzid)) {
String[] val = rb.getStringArray(tzid);
val[0] = alias;
value.add(val);
}
}
}
}
return value.toArray(new String[0][]);
}
String[] getCalendarNames(String key) {
String[] names = null;
String cacheKey = CALENDAR_NAMES + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data == null || ((names = (String[]) data.get()) == null)) {
ResourceBundle rb = localeData.getDateFormatData(locale);
if (rb.containsKey(key)) {
names = rb.getStringArray(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) names, referenceQueue));
}
}
return names;
}
String[] getJavaTimeNames(String key) {
String[] names = null;
String cacheKey = CALENDAR_NAMES + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
if (data == null || ((names = (String[]) data.get()) == null)) {
ResourceBundle rb = getJavaTimeFormatData();
if (rb.containsKey(key)) {
names = rb.getStringArray(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) names, referenceQueue));
}
}
return names;
}
public String getDateTimePattern(int timeStyle, int dateStyle, Calendar cal) {
if (cal == null) {
cal = Calendar.getInstance(locale);
}
return getDateTimePattern(null, timeStyle, dateStyle, cal.getCalendarType());
}
/**
* Returns a date-time format pattern
* @param timeStyle style of time; one of FULL, LONG, MEDIUM, SHORT in DateFormat,
* or -1 if not required
* @param dateStyle style of time; one of FULL, LONG, MEDIUM, SHORT in DateFormat,
* or -1 if not required
* @param calType the calendar type for the pattern
* @return the pattern string
*/
public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType) {
calType = CalendarDataUtility.normalizeCalendarType(calType);
String pattern;
pattern = getDateTimePattern("java.time.", timeStyle, dateStyle, calType);
if (pattern == null) {
pattern = getDateTimePattern(null, timeStyle, dateStyle, calType);
}
return pattern;
}
private String getDateTimePattern(String prefix, int timeStyle, int dateStyle, String calType) {
String pattern;
String timePattern = null;
String datePattern = null;
if (timeStyle >= 0) {
if (prefix != null) {
timePattern = getDateTimePattern(prefix, "TimePatterns", timeStyle, calType);
}
if (timePattern == null) {
timePattern = getDateTimePattern(null, "TimePatterns", timeStyle, calType);
}
}
if (dateStyle >= 0) {
if (prefix != null) {
datePattern = getDateTimePattern(prefix, "DatePatterns", dateStyle, calType);
}
if (datePattern == null) {
datePattern = getDateTimePattern(null, "DatePatterns", dateStyle, calType);
}
}
if (timeStyle >= 0) {
if (dateStyle >= 0) {
String dateTimePattern = null;
if (prefix != null) {
dateTimePattern = getDateTimePattern(prefix, "DateTimePatterns", 0, calType);
}
if (dateTimePattern == null) {
dateTimePattern = getDateTimePattern(null, "DateTimePatterns", 0, calType);
}
switch (dateTimePattern) {
case "{1} {0}":
pattern = datePattern + " " + timePattern;
break;
case "{0} {1}":
pattern = timePattern + " " + datePattern;
break;
default:
pattern = MessageFormat.format(dateTimePattern, timePattern, datePattern);
break;
}
} else {
pattern = timePattern;
}
} else if (dateStyle >= 0) {
pattern = datePattern;
} else {
throw new IllegalArgumentException("No date or time style specified");
}
return pattern;
}
public String[] getNumberPatterns() {
String[] numberPatterns = null;
removeEmptyReferences();
ResourceReference data = cache.get(NUMBER_PATTERNS_CACHEKEY);
if (data == null || ((numberPatterns = (String[]) data.get()) == null)) {
ResourceBundle resource = localeData.getNumberFormatData(locale);
numberPatterns = resource.getStringArray("NumberPatterns");
cache.put(NUMBER_PATTERNS_CACHEKEY,
new ResourceReference(NUMBER_PATTERNS_CACHEKEY, (Object) numberPatterns, referenceQueue));
}
return numberPatterns;
}
/**
* Returns the FormatData resource bundle of this LocaleResources.
* The FormatData should be used only for accessing extra
* resources required by JSR 310.
*/
public ResourceBundle getJavaTimeFormatData() {
ResourceBundle rb = localeData.getDateFormatData(locale);
if (rb instanceof ParallelListResourceBundle) {
localeData.setSupplementary((ParallelListResourceBundle) rb);
}
return rb;
}
private String getDateTimePattern(String prefix, String key, int styleIndex, String calendarType) {
StringBuilder sb = new StringBuilder();
if (prefix != null) {
sb.append(prefix);
}
if (!"gregory".equals(calendarType)) {
sb.append(calendarType).append('.');
}
sb.append(key);
String resourceKey = sb.toString();
String cacheKey = sb.insert(0, DATE_TIME_PATTERN).toString();
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
Object value = NULLOBJECT;
if (data == null || ((value = data.get()) == null)) {
ResourceBundle r = (prefix != null) ? getJavaTimeFormatData() : localeData.getDateFormatData(locale);
if (r.containsKey(resourceKey)) {
value = r.getStringArray(resourceKey);
} else {
assert !resourceKey.equals(key);
if (r.containsKey(key)) {
value = r.getStringArray(key);
}
}
cache.put(cacheKey,
new ResourceReference(cacheKey, value, referenceQueue));
}
if (value == NULLOBJECT) {
assert prefix != null;
return null;
}
return ((String[])value)[styleIndex];
}
private static class ResourceReference extends SoftReference<Object> {
private final String cacheKey;
ResourceReference(String cacheKey, Object o, ReferenceQueue<Object> q) {
super(o, q);
this.cacheKey = cacheKey;
}
String getCacheKey() {
return cacheKey;
}
}
}

View File

@@ -0,0 +1,419 @@
/*
* Copyright (c) 2005, 2013, 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.
*/
package sun.util.locale.provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.IllformedLocaleException;
import java.util.List;
import java.util.Locale;
import java.util.Locale.Builder;
import java.util.ResourceBundle.Control;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.LocaleServiceProvider;
import sun.util.logging.PlatformLogger;
/**
* An instance of this class holds a set of the third party implementations of a particular
* locale sensitive service, such as {@link java.util.spi.LocaleNameProvider}.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public final class LocaleServiceProviderPool {
/**
* A Map that holds singleton instances of this class. Each instance holds a
* set of provider implementations of a particular locale sensitive service.
*/
private static ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProviderPool> poolOfPools =
new ConcurrentHashMap<>();
/**
* A Map containing locale service providers that implement the
* specified provider SPI, keyed by a LocaleProviderAdapter.Type
*/
private ConcurrentMap<LocaleProviderAdapter.Type, LocaleServiceProvider> providers =
new ConcurrentHashMap<>();
/**
* A Map that retains Locale->provider mapping
*/
private ConcurrentMap<Locale, List<LocaleProviderAdapter.Type>> providersCache =
new ConcurrentHashMap<>();
/**
* Available locales for this locale sensitive service. This also contains
* JRE's available locales
*/
private Set<Locale> availableLocales = null;
/**
* Provider class
*/
private Class<? extends LocaleServiceProvider> providerClass;
/**
* Array of all Locale Sensitive SPI classes.
*
* We know "spiClasses" contains classes that extends LocaleServiceProvider,
* but generic array creation is not allowed, thus the "unchecked" warning
* is suppressed here.
*/
@SuppressWarnings("unchecked")
static final Class<LocaleServiceProvider>[] spiClasses =
(Class<LocaleServiceProvider>[]) new Class<?>[] {
java.text.spi.BreakIteratorProvider.class,
java.text.spi.CollatorProvider.class,
java.text.spi.DateFormatProvider.class,
java.text.spi.DateFormatSymbolsProvider.class,
java.text.spi.DecimalFormatSymbolsProvider.class,
java.text.spi.NumberFormatProvider.class,
java.util.spi.CurrencyNameProvider.class,
java.util.spi.LocaleNameProvider.class,
java.util.spi.TimeZoneNameProvider.class,
java.util.spi.CalendarDataProvider.class
};
/**
* A factory method that returns a singleton instance
*/
public static LocaleServiceProviderPool getPool(Class<? extends LocaleServiceProvider> providerClass) {
LocaleServiceProviderPool pool = poolOfPools.get(providerClass);
if (pool == null) {
LocaleServiceProviderPool newPool =
new LocaleServiceProviderPool(providerClass);
pool = poolOfPools.putIfAbsent(providerClass, newPool);
if (pool == null) {
pool = newPool;
}
}
return pool;
}
/**
* The sole constructor.
*
* @param c class of the locale sensitive service
*/
private LocaleServiceProviderPool (final Class<? extends LocaleServiceProvider> c) {
providerClass = c;
for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {
LocaleProviderAdapter lda = LocaleProviderAdapter.forType(type);
if (lda != null) {
LocaleServiceProvider provider = lda.getLocaleServiceProvider(c);
if (provider != null) {
providers.putIfAbsent(type, provider);
}
}
}
}
static void config(Class<? extends Object> caller, String message) {
PlatformLogger logger = PlatformLogger.getLogger(caller.getCanonicalName());
logger.config(message);
}
/**
* Lazy loaded set of available locales.
* Loading all locales is a very long operation.
*/
private static class AllAvailableLocales {
/**
* Available locales for all locale sensitive services.
* This also contains JRE's available locales
*/
static final Locale[] allAvailableLocales;
static {
Set<Locale> all = new HashSet<>();
for (Class<? extends LocaleServiceProvider> c : spiClasses) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(c);
all.addAll(pool.getAvailableLocaleSet());
}
allAvailableLocales = all.toArray(new Locale[0]);
}
// No instantiation
private AllAvailableLocales() {
}
}
/**
* Returns an array of available locales for all the provider classes.
* This array is a merged array of all the locales that are provided by each
* provider, including the JRE.
*
* @return an array of the available locales for all provider classes
*/
public static Locale[] getAllAvailableLocales() {
return AllAvailableLocales.allAvailableLocales.clone();
}
/**
* Returns an array of available locales. This array is a
* merged array of all the locales that are provided by each
* provider, including the JRE.
*
* @return an array of the available locales
*/
public Locale[] getAvailableLocales() {
Set<Locale> locList = new HashSet<>();
locList.addAll(getAvailableLocaleSet());
// Make sure it all contains JRE's locales for compatibility.
locList.addAll(Arrays.asList(LocaleProviderAdapter.forJRE().getAvailableLocales()));
Locale[] tmp = new Locale[locList.size()];
locList.toArray(tmp);
return tmp;
}
/**
* Returns the union of locale sets that are available from
* each service provider. This method does NOT return the
* defensive copy.
*
* @return a set of available locales
*/
private synchronized Set<Locale> getAvailableLocaleSet() {
if (availableLocales == null) {
availableLocales = new HashSet<>();
for (LocaleServiceProvider lsp : providers.values()) {
Locale[] locales = lsp.getAvailableLocales();
for (Locale locale: locales) {
availableLocales.add(getLookupLocale(locale));
}
}
}
return availableLocales;
}
/**
* Returns whether any provider for this locale sensitive
* service is available or not, excluding JRE's one.
*
* @return true if any provider (other than JRE) is available
*/
boolean hasProviders() {
return providers.size() != 1 ||
(providers.get(LocaleProviderAdapter.Type.JRE) == null &&
providers.get(LocaleProviderAdapter.Type.FALLBACK) == null);
}
/**
* Returns the provider's localized object for the specified
* locale.
*
* @param getter an object on which getObject() method
* is called to obtain the provider's instance.
* @param locale the given locale that is used as the starting one
* @param params provider specific parameters
* @return provider's instance, or null.
*/
public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
Locale locale,
Object... params) {
return getLocalizedObjectImpl(getter, locale, true, null, params);
}
/**
* Returns the provider's localized name for the specified
* locale.
*
* @param getter an object on which getObject() method
* is called to obtain the provider's instance.
* @param locale the given locale that is used as the starting one
* @param key the key string for name providers
* @param params provider specific parameters
* @return provider's instance, or null.
*/
public <P extends LocaleServiceProvider, S> S getLocalizedObject(LocalizedObjectGetter<P, S> getter,
Locale locale,
String key,
Object... params) {
return getLocalizedObjectImpl(getter, locale, false, key, params);
}
@SuppressWarnings("unchecked")
private <P extends LocaleServiceProvider, S> S getLocalizedObjectImpl(LocalizedObjectGetter<P, S> getter,
Locale locale,
boolean isObjectProvider,
String key,
Object... params) {
if (locale == null) {
throw new NullPointerException();
}
// Check whether JRE is the sole locale data provider or not,
// and directly call it if it is.
if (!hasProviders()) {
return getter.getObject((P)providers.get(LocaleProviderAdapter.defaultLocaleProviderAdapter),
locale, key, params);
}
List<Locale> lookupLocales = getLookupLocales(locale);
Set<Locale> available = getAvailableLocaleSet();
for (Locale current : lookupLocales) {
if (available.contains(current)) {
S providersObj;
for (LocaleProviderAdapter.Type type: findProviders(current)) {
LocaleServiceProvider lsp = providers.get(type);
providersObj = getter.getObject((P)lsp, locale, key, params);
if (providersObj != null) {
return providersObj;
} else if (isObjectProvider) {
config(LocaleServiceProviderPool.class,
"A locale sensitive service provider returned null for a localized objects, which should not happen. provider: "
+ lsp + " locale: " + locale);
}
}
}
}
// not found.
return null;
}
/**
* Returns the list of locale service provider instances that support
* the specified locale.
*
* @param locale the given locale
* @return the list of locale data adapter types
*/
private List<LocaleProviderAdapter.Type> findProviders(Locale locale) {
List<LocaleProviderAdapter.Type> providersList = providersCache.get(locale);
if (providersList == null) {
for (LocaleProviderAdapter.Type type : LocaleProviderAdapter.getAdapterPreference()) {
LocaleServiceProvider lsp = providers.get(type);
if (lsp != null) {
if (lsp.isSupportedLocale(locale)) {
if (providersList == null) {
providersList = new ArrayList<>(2);
}
providersList.add(type);
}
}
}
if (providersList == null) {
providersList = NULL_LIST;
}
List<LocaleProviderAdapter.Type> val = providersCache.putIfAbsent(locale, providersList);
if (val != null) {
providersList = val;
}
}
return providersList;
}
/**
* Returns a list of candidate locales for service look up.
* @param locale the input locale
* @return the list of candidate locales for the given locale
*/
static List<Locale> getLookupLocales(Locale locale) {
// Note: We currently use the default implementation of
// ResourceBundle.Control.getCandidateLocales. The result
// returned by getCandidateLocales are already normalized
// (no extensions) for service look up.
List<Locale> lookupLocales = Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
.getCandidateLocales("", locale);
return lookupLocales;
}
/**
* Returns an instance of Locale used for service look up.
* The result Locale has no extensions except for ja_JP_JP
* and th_TH_TH
*
* @param locale the locale
* @return the locale used for service look up
*/
static Locale getLookupLocale(Locale locale) {
Locale lookupLocale = locale;
if (locale.hasExtensions()
&& !locale.equals(JRELocaleConstants.JA_JP_JP)
&& !locale.equals(JRELocaleConstants.TH_TH_TH)) {
// remove extensions
Builder locbld = new Builder();
try {
locbld.setLocale(locale);
locbld.clearExtensions();
lookupLocale = locbld.build();
} catch (IllformedLocaleException e) {
// A Locale with non-empty extensions
// should have well-formed fields except
// for ja_JP_JP and th_TH_TH. Therefore,
// it should never enter in this catch clause.
config(LocaleServiceProviderPool.class,
"A locale(" + locale + ") has non-empty extensions, but has illformed fields.");
// Fallback - script field will be lost.
lookupLocale = new Locale(locale.getLanguage(), locale.getCountry(), locale.getVariant());
}
}
return lookupLocale;
}
/**
* A dummy locale service provider list that indicates there is no
* provider available
*/
private static List<LocaleProviderAdapter.Type> NULL_LIST =
Collections.emptyList();
/**
* An interface to get a localized object for each locale sensitive
* service class.
*/
public interface LocalizedObjectGetter<P extends LocaleServiceProvider, S> {
/**
* Returns an object from the provider
*
* @param lsp the provider
* @param locale the locale
* @param key key string to localize, or null if the provider is not
* a name provider
* @param params provider specific params
* @return localized object from the provider
*/
public S getObject(P lsp,
Locale locale,
String key,
Object... params);
}
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright (c) 1999, 2012, 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.
*/
/*
*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package sun.util.locale.provider;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.spi.NumberFormatProvider;
import java.util.Currency;
import java.util.Locale;
import java.util.Set;
/**
* Concrete implementation of the {@link java.text.spi.NumberFormatProvider
* NumberFormatProvider} class for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class NumberFormatProviderImpl extends NumberFormatProvider implements AvailableLanguageTags {
// Constants used by factory methods to specify a style of format.
private static final int NUMBERSTYLE = 0;
private static final int CURRENCYSTYLE = 1;
private static final int PERCENTSTYLE = 2;
private static final int SCIENTIFICSTYLE = 3;
private static final int INTEGERSTYLE = 4;
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
public NumberFormatProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.forType(type).getAvailableLocales();
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a new <code>NumberFormat</code> instance which formats
* monetary values for the specified locale.
*
* @param locale the desired locale.
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a currency formatter
* @see java.text.NumberFormat#getCurrencyInstance(java.util.Locale)
*/
@Override
public NumberFormat getCurrencyInstance(Locale locale) {
return getInstance(locale, CURRENCYSTYLE);
}
/**
* Returns a new <code>NumberFormat</code> instance which formats
* integer values for the specified locale.
* The returned number format is configured to
* round floating point numbers to the nearest integer using
* half-even rounding (see {@link java.math.RoundingMode#HALF_EVEN HALF_EVEN})
* for formatting, and to parse only the integer part of
* an input string (see {@link
* java.text.NumberFormat#isParseIntegerOnly isParseIntegerOnly}).
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a number format for integer values
* @see java.text.NumberFormat#getIntegerInstance(java.util.Locale)
*/
@Override
public NumberFormat getIntegerInstance(Locale locale) {
return getInstance(locale, INTEGERSTYLE);
}
/**
* Returns a new general-purpose <code>NumberFormat</code> instance for
* the specified locale.
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a general-purpose number formatter
* @see java.text.NumberFormat#getNumberInstance(java.util.Locale)
*/
@Override
public NumberFormat getNumberInstance(Locale locale) {
return getInstance(locale, NUMBERSTYLE);
}
/**
* Returns a new <code>NumberFormat</code> instance which formats
* percentage values for the specified locale.
*
* @param locale the desired locale
* @exception NullPointerException if <code>locale</code> is null
* @exception IllegalArgumentException if <code>locale</code> isn't
* one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @return a percent formatter
* @see java.text.NumberFormat#getPercentInstance(java.util.Locale)
*/
@Override
public NumberFormat getPercentInstance(Locale locale) {
return getInstance(locale, PERCENTSTYLE);
}
private NumberFormat getInstance(Locale locale,
int choice) {
if (locale == null) {
throw new NullPointerException();
}
LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
String[] numberPatterns = adapter.getLocaleResources(locale).getNumberPatterns();
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice;
DecimalFormat format = new DecimalFormat(numberPatterns[entry], symbols);
if (choice == INTEGERSTYLE) {
format.setMaximumFractionDigits(0);
format.setDecimalSeparatorAlwaysShown(false);
format.setParseIntegerOnly(true);
} else if (choice == CURRENCYSTYLE) {
adjustForCurrencyDefaultFractionDigits(format, symbols);
}
return format;
}
/**
* Adjusts the minimum and maximum fraction digits to values that
* are reasonable for the currency's default fraction digits.
*/
private static void adjustForCurrencyDefaultFractionDigits(
DecimalFormat format, DecimalFormatSymbols symbols) {
Currency currency = symbols.getCurrency();
if (currency == null) {
try {
currency = Currency.getInstance(symbols.getInternationalCurrencySymbol());
} catch (IllegalArgumentException e) {
}
}
if (currency != null) {
int digits = currency.getDefaultFractionDigits();
if (digits != -1) {
int oldMinDigits = format.getMinimumFractionDigits();
// Common patterns are "#.##", "#.00", "#".
// Try to adjust all of them in a reasonable way.
if (oldMinDigits == format.getMaximumFractionDigits()) {
format.setMinimumFractionDigits(digits);
format.setMaximumFractionDigits(digits);
} else {
format.setMinimumFractionDigits(Math.min(digits, oldMinDigits));
format.setMaximumFractionDigits(digits);
}
}
}
}
@Override
public Set<String> getAvailableLanguageTags() {
return langtags;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
package sun.util.locale.provider;
import sun.util.resources.LocaleData;
/**
* Accessor for LocaleData
*
* @author Naoto Sato
*/
public interface ResourceBundleBasedAdapter {
public LocaleData getLocaleData();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,615 @@
/*
* Copyright (c) 2012, 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.
*/
package sun.util.locale.provider;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.BreakIterator;
import java.text.Collator;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.spi.BreakIteratorProvider;
import java.text.spi.CollatorProvider;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Locale;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import java.util.spi.LocaleServiceProvider;
import java.util.spi.TimeZoneNameProvider;
/**
* LocaleProviderAdapter implementation for the installed SPI implementations.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class SPILocaleProviderAdapter extends AuxLocaleProviderAdapter {
/**
* Returns the type of this LocaleProviderAdapter
*/
@Override
public LocaleProviderAdapter.Type getAdapterType() {
return LocaleProviderAdapter.Type.SPI;
}
@Override
protected <P extends LocaleServiceProvider> P findInstalledProvider(final Class<P> c) {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<P>() {
@Override
@SuppressWarnings("unchecked")
public P run() {
P delegate = null;
for (LocaleServiceProvider provider : ServiceLoader.loadInstalled(c)) {
if (delegate == null) {
try {
delegate =
(P) Class.forName(SPILocaleProviderAdapter.class.getCanonicalName() +
"$" +
c.getSimpleName() +
"Delegate")
.newInstance();
} catch (ClassNotFoundException |
InstantiationException |
IllegalAccessException e) {
LocaleServiceProviderPool.config(SPILocaleProviderAdapter.class, e.toString());
return null;
}
}
((Delegate)delegate).addImpl(provider);
}
return delegate;
}
});
} catch (PrivilegedActionException e) {
LocaleServiceProviderPool.config(SPILocaleProviderAdapter.class, e.toString());
}
return null;
}
/*
* Delegate interface. All the implementations have to have the class name
* following "<provider class name>Delegate" convention.
*/
interface Delegate<P extends LocaleServiceProvider> {
public void addImpl(P impl);
public P getImpl(Locale locale);
}
/*
* Obtain the real SPI implementation, using locale fallback
*/
private static <P extends LocaleServiceProvider> P getImpl(Map<Locale, P> map, Locale locale) {
for (Locale l : LocaleServiceProviderPool.getLookupLocales(locale)) {
P ret = map.get(l);
if (ret != null) {
return ret;
}
}
return null;
}
/*
* Delegates for the actual SPI implementations.
*/
static class BreakIteratorProviderDelegate extends BreakIteratorProvider
implements Delegate<BreakIteratorProvider> {
private ConcurrentMap<Locale, BreakIteratorProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(BreakIteratorProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public BreakIteratorProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public BreakIterator getWordInstance(Locale locale) {
BreakIteratorProvider bip = getImpl(locale);
assert bip != null;
return bip.getWordInstance(locale);
}
@Override
public BreakIterator getLineInstance(Locale locale) {
BreakIteratorProvider bip = getImpl(locale);
assert bip != null;
return bip.getLineInstance(locale);
}
@Override
public BreakIterator getCharacterInstance(Locale locale) {
BreakIteratorProvider bip = getImpl(locale);
assert bip != null;
return bip.getCharacterInstance(locale);
}
@Override
public BreakIterator getSentenceInstance(Locale locale) {
BreakIteratorProvider bip = getImpl(locale);
assert bip != null;
return bip.getSentenceInstance(locale);
}
}
static class CollatorProviderDelegate extends CollatorProvider implements Delegate<CollatorProvider> {
private ConcurrentMap<Locale, CollatorProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(CollatorProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public CollatorProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public Collator getInstance(Locale locale) {
CollatorProvider cp = getImpl(locale);
assert cp != null;
return cp.getInstance(locale);
}
}
static class DateFormatProviderDelegate extends DateFormatProvider
implements Delegate<DateFormatProvider> {
private ConcurrentMap<Locale, DateFormatProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(DateFormatProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public DateFormatProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public DateFormat getTimeInstance(int style, Locale locale) {
DateFormatProvider dfp = getImpl(locale);
assert dfp != null;
return dfp.getTimeInstance(style, locale);
}
@Override
public DateFormat getDateInstance(int style, Locale locale) {
DateFormatProvider dfp = getImpl(locale);
assert dfp != null;
return dfp.getDateInstance(style, locale);
}
@Override
public DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) {
DateFormatProvider dfp = getImpl(locale);
assert dfp != null;
return dfp.getDateTimeInstance(dateStyle, timeStyle, locale);
}
}
static class DateFormatSymbolsProviderDelegate extends DateFormatSymbolsProvider
implements Delegate<DateFormatSymbolsProvider> {
private ConcurrentMap<Locale, DateFormatSymbolsProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(DateFormatSymbolsProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public DateFormatSymbolsProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public DateFormatSymbols getInstance(Locale locale) {
DateFormatSymbolsProvider dfsp = getImpl(locale);
assert dfsp != null;
return dfsp.getInstance(locale);
}
}
static class DecimalFormatSymbolsProviderDelegate extends DecimalFormatSymbolsProvider
implements Delegate<DecimalFormatSymbolsProvider> {
private ConcurrentMap<Locale, DecimalFormatSymbolsProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(DecimalFormatSymbolsProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public DecimalFormatSymbolsProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public DecimalFormatSymbols getInstance(Locale locale) {
DecimalFormatSymbolsProvider dfsp = getImpl(locale);
assert dfsp != null;
return dfsp.getInstance(locale);
}
}
static class NumberFormatProviderDelegate extends NumberFormatProvider
implements Delegate<NumberFormatProvider> {
private ConcurrentMap<Locale, NumberFormatProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(NumberFormatProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public NumberFormatProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public NumberFormat getCurrencyInstance(Locale locale) {
NumberFormatProvider nfp = getImpl(locale);
assert nfp != null;
return nfp.getCurrencyInstance(locale);
}
@Override
public NumberFormat getIntegerInstance(Locale locale) {
NumberFormatProvider nfp = getImpl(locale);
assert nfp != null;
return nfp.getIntegerInstance(locale);
}
@Override
public NumberFormat getNumberInstance(Locale locale) {
NumberFormatProvider nfp = getImpl(locale);
assert nfp != null;
return nfp.getNumberInstance(locale);
}
@Override
public NumberFormat getPercentInstance(Locale locale) {
NumberFormatProvider nfp = getImpl(locale);
assert nfp != null;
return nfp.getPercentInstance(locale);
}
}
static class CalendarDataProviderDelegate extends CalendarDataProvider
implements Delegate<CalendarDataProvider> {
private ConcurrentMap<Locale, CalendarDataProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(CalendarDataProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public CalendarDataProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public int getFirstDayOfWeek(Locale locale) {
CalendarDataProvider cdp = getImpl(locale);
assert cdp != null;
return cdp.getFirstDayOfWeek(locale);
}
@Override
public int getMinimalDaysInFirstWeek(Locale locale) {
CalendarDataProvider cdp = getImpl(locale);
assert cdp != null;
return cdp.getMinimalDaysInFirstWeek(locale);
}
}
static class CalendarNameProviderDelegate extends CalendarNameProvider
implements Delegate<CalendarNameProvider> {
private ConcurrentMap<Locale, CalendarNameProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(CalendarNameProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public CalendarNameProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public String getDisplayName(String calendarType,
int field, int value,
int style, Locale locale) {
CalendarNameProvider cdp = getImpl(locale);
assert cdp != null;
return cdp.getDisplayName(calendarType, field, value, style, locale);
}
@Override
public Map<String, Integer> getDisplayNames(String calendarType,
int field, int style,
Locale locale) {
CalendarNameProvider cdp = getImpl(locale);
assert cdp != null;
return cdp.getDisplayNames(calendarType, field, style, locale);
}
}
static class CurrencyNameProviderDelegate extends CurrencyNameProvider
implements Delegate<CurrencyNameProvider> {
private ConcurrentMap<Locale, CurrencyNameProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(CurrencyNameProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public CurrencyNameProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public String getSymbol(String currencyCode, Locale locale) {
CurrencyNameProvider cnp = getImpl(locale);
assert cnp != null;
return cnp.getSymbol(currencyCode, locale);
}
@Override
public String getDisplayName(String currencyCode, Locale locale) {
CurrencyNameProvider cnp = getImpl(locale);
assert cnp != null;
return cnp.getDisplayName(currencyCode, locale);
}
}
static class LocaleNameProviderDelegate extends LocaleNameProvider
implements Delegate<LocaleNameProvider> {
private ConcurrentMap<Locale, LocaleNameProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(LocaleNameProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public LocaleNameProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public String getDisplayLanguage(String languageCode, Locale locale) {
LocaleNameProvider lnp = getImpl(locale);
assert lnp != null;
return lnp.getDisplayLanguage(languageCode, locale);
}
@Override
public String getDisplayScript(String scriptCode, Locale locale) {
LocaleNameProvider lnp = getImpl(locale);
assert lnp != null;
return lnp.getDisplayScript(scriptCode, locale);
}
@Override
public String getDisplayCountry(String countryCode, Locale locale) {
LocaleNameProvider lnp = getImpl(locale);
assert lnp != null;
return lnp.getDisplayCountry(countryCode, locale);
}
@Override
public String getDisplayVariant(String variant, Locale locale) {
LocaleNameProvider lnp = getImpl(locale);
assert lnp != null;
return lnp.getDisplayVariant(variant, locale);
}
}
static class TimeZoneNameProviderDelegate extends TimeZoneNameProvider
implements Delegate<TimeZoneNameProvider> {
private ConcurrentMap<Locale, TimeZoneNameProvider> map = new ConcurrentHashMap<>();
@Override
public void addImpl(TimeZoneNameProvider impl) {
for (Locale l : impl.getAvailableLocales()) {
map.putIfAbsent(l, impl);
}
}
@Override
public TimeZoneNameProvider getImpl(Locale locale) {
return SPILocaleProviderAdapter.getImpl(map, locale);
}
@Override
public Locale[] getAvailableLocales() {
return map.keySet().toArray(new Locale[0]);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return map.containsKey(locale);
}
@Override
public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
TimeZoneNameProvider tznp = getImpl(locale);
assert tznp != null;
return tznp.getDisplayName(ID, daylight, style, locale);
}
@Override
public String getGenericDisplayName(String ID, int style, Locale locale) {
TimeZoneNameProvider tznp = getImpl(locale);
assert tznp != null;
return tznp.getGenericDisplayName(ID, style, locale);
}
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2012, 2013, 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.
*/
package sun.util.locale.provider;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.spi.TimeZoneNameProvider;
/**
* Concrete implementation of the
* {@link java.util.spi.TimeZoneNameProvider TimeZoneNameProvider} class
* for the JRE LocaleProviderAdapter.
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public class TimeZoneNameProviderImpl extends TimeZoneNameProvider {
private final LocaleProviderAdapter.Type type;
private final Set<String> langtags;
TimeZoneNameProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
this.type = type;
this.langtags = langtags;
}
/**
* Returns an array of all locales for which this locale service provider
* can provide localized objects or names.
*
* @return An array of all locales for which this locale service provider
* can provide localized objects or names.
*/
@Override
public Locale[] getAvailableLocales() {
return LocaleProviderAdapter.toLocaleArray(langtags);
}
@Override
public boolean isSupportedLocale(Locale locale) {
return LocaleProviderAdapter.isSupportedLocale(locale, type, langtags);
}
/**
* Returns a name for the given time zone ID that's suitable for
* presentation to the user in the specified locale. The given time
* zone ID is "GMT" or one of the names defined using "Zone" entries
* in the "tz database", a public domain time zone database at
* <a href="ftp://elsie.nci.nih.gov/pub/">ftp://elsie.nci.nih.gov/pub/</a>.
* The data of this database is contained in a file whose name starts with
* "tzdata", and the specification of the data format is part of the zic.8
* man page, which is contained in a file whose name starts with "tzcode".
* <p>
* If <code>daylight</code> is true, the method should return a name
* appropriate for daylight saving time even if the specified time zone
* has not observed daylight saving time in the past.
*
* @param ID a time zone ID string
* @param daylight if true, return the daylight saving name.
* @param style either {@link java.util.TimeZone#LONG TimeZone.LONG} or
* {@link java.util.TimeZone#SHORT TimeZone.SHORT}
* @param locale the desired locale
* @return the human-readable name of the given time zone in the
* given locale, or null if it's not available.
* @exception IllegalArgumentException if <code>style</code> is invalid,
* or <code>locale</code> isn't one of the locales returned from
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
* getAvailableLocales()}.
* @exception NullPointerException if <code>ID</code> or <code>locale</code>
* is null
* @see java.util.TimeZone#getDisplayName(boolean, int, java.util.Locale)
*/
@Override
public String getDisplayName(String id, boolean daylight, int style, Locale locale) {
String[] names = getDisplayNameArray(id, locale);
if (Objects.nonNull(names)) {
assert names.length >= 7;
int index = daylight ? 3 : 1;
if (style == TimeZone.SHORT) {
index++;
}
return names[index];
}
return null;
}
@Override
public String getGenericDisplayName(String id, int style, Locale locale) {
String[] names = getDisplayNameArray(id, locale);
if (Objects.nonNull(names)) {
assert names.length >= 7;
return names[(style == TimeZone.LONG) ? 5 : 6];
}
return null;
}
private String[] getDisplayNameArray(String id, Locale locale) {
Objects.requireNonNull(id);
Objects.requireNonNull(locale);
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getTimeZoneNames(id);
}
/**
* Returns a String[][] as the DateFormatSymbols.getZoneStrings() value for
* the given locale. This method is package private.
*
* @param locale a Locale for time zone names
* @return an array of time zone names arrays
*/
String[][] getZoneStrings(Locale locale) {
return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getZoneStrings();
}
}

View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2005, 2013, 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.
*/
package sun.util.locale.provider;
import java.lang.ref.SoftReference;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.spi.TimeZoneNameProvider;
import sun.util.calendar.ZoneInfo;
/**
* Utility class that deals with the localized time zone names
*
* @author Naoto Sato
* @author Masayoshi Okutsu
*/
public final class TimeZoneNameUtility {
/**
* cache to hold time zone localized strings. Keyed by Locale
*/
private static ConcurrentHashMap<Locale, SoftReference<String[][]>> cachedZoneData =
new ConcurrentHashMap<>();
/**
* Cache for managing display names per timezone per locale
* The structure is:
* Map(key=id, value=SoftReference(Map(key=locale, value=displaynames)))
*/
private static final Map<String, SoftReference<Map<Locale, String[]>>> cachedDisplayNames =
new ConcurrentHashMap<>();
/**
* get time zone localized strings. Enumerate all keys.
*/
public static String[][] getZoneStrings(Locale locale) {
String[][] zones;
SoftReference<String[][]> data = cachedZoneData.get(locale);
if (data == null || ((zones = data.get()) == null)) {
zones = loadZoneStrings(locale);
data = new SoftReference<>(zones);
cachedZoneData.put(locale, data);
}
return zones;
}
private static String[][] loadZoneStrings(Locale locale) {
// If the provider is a TimeZoneNameProviderImpl, call its getZoneStrings
// in order to avoid per-ID retrieval.
LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(TimeZoneNameProvider.class, locale);
TimeZoneNameProvider provider = adapter.getTimeZoneNameProvider();
if (provider instanceof TimeZoneNameProviderImpl) {
return ((TimeZoneNameProviderImpl)provider).getZoneStrings(locale);
}
// Performs per-ID retrieval.
Set<String> zoneIDs = LocaleProviderAdapter.forJRE().getLocaleResources(locale).getZoneIDs();
List<String[]> zones = new LinkedList<>();
for (String key : zoneIDs) {
String[] names = retrieveDisplayNamesImpl(key, locale);
if (names != null) {
zones.add(names);
}
}
String[][] zonesArray = new String[zones.size()][];
return zones.toArray(zonesArray);
}
/**
* Retrieve display names for a time zone ID.
*/
public static String[] retrieveDisplayNames(String id, Locale locale) {
Objects.requireNonNull(id);
Objects.requireNonNull(locale);
return retrieveDisplayNamesImpl(id, locale);
}
/**
* Retrieves a generic time zone display name for a time zone ID.
*
* @param id time zone ID
* @param style TimeZone.LONG or TimeZone.SHORT
* @param locale desired Locale
* @return the requested generic time zone display name, or null if not found.
*/
public static String retrieveGenericDisplayName(String id, int style, Locale locale) {
String[] names = retrieveDisplayNamesImpl(id, locale);
if (Objects.nonNull(names)) {
return names[6 - style];
} else {
return null;
}
}
/**
* Retrieves a standard or daylight-saving time name for the given time zone ID.
*
* @param id time zone ID
* @param daylight true for a daylight saving time name, or false for a standard time name
* @param style TimeZone.LONG or TimeZone.SHORT
* @param locale desired Locale
* @return the requested time zone name, or null if not found.
*/
public static String retrieveDisplayName(String id, boolean daylight, int style, Locale locale) {
String[] names = retrieveDisplayNamesImpl(id, locale);
if (Objects.nonNull(names)) {
return names[(daylight ? 4 : 2) - style];
} else {
return null;
}
}
private static String[] retrieveDisplayNamesImpl(String id, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
String[] names;
Map<Locale, String[]> perLocale = null;
SoftReference<Map<Locale, String[]>> ref = cachedDisplayNames.get(id);
if (Objects.nonNull(ref)) {
perLocale = ref.get();
if (Objects.nonNull(perLocale)) {
names = perLocale.get(locale);
if (Objects.nonNull(names)) {
return names;
}
}
}
// build names array
names = new String[7];
names[0] = id;
for (int i = 1; i <= 6; i ++) {
names[i] = pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale,
i<5 ? (i<3 ? "std" : "dst") : "generic", i%2, id);
}
if (Objects.isNull(perLocale)) {
perLocale = new ConcurrentHashMap<>();
}
perLocale.put(locale, names);
ref = new SoftReference<>(perLocale);
cachedDisplayNames.put(id, ref);
return names;
}
/**
* Obtains a localized time zone strings from a TimeZoneNameProvider
* implementation.
*/
private static class TimeZoneNameGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider,
String> {
private static final TimeZoneNameGetter INSTANCE =
new TimeZoneNameGetter();
@Override
public String getObject(TimeZoneNameProvider timeZoneNameProvider,
Locale locale,
String requestID,
Object... params) {
assert params.length == 2;
int style = (int) params[0];
String tzid = (String) params[1];
String value = getName(timeZoneNameProvider, locale, requestID, style, tzid);
if (value == null) {
Map<String, String> aliases = ZoneInfo.getAliasTable();
if (aliases != null) {
String canonicalID = aliases.get(tzid);
if (canonicalID != null) {
value = getName(timeZoneNameProvider, locale, requestID, style, canonicalID);
}
if (value == null) {
value = examineAliases(timeZoneNameProvider, locale, requestID,
canonicalID != null ? canonicalID : tzid, style, aliases);
}
}
}
return value;
}
private static String examineAliases(TimeZoneNameProvider tznp, Locale locale,
String requestID, String tzid, int style,
Map<String, String> aliases) {
for (Map.Entry<String, String> entry : aliases.entrySet()) {
if (entry.getValue().equals(tzid)) {
String alias = entry.getKey();
String name = getName(tznp, locale, requestID, style, alias);
if (name != null) {
return name;
}
name = examineAliases(tznp, locale, requestID, alias, style, aliases);
if (name != null) {
return name;
}
}
}
return null;
}
private static String getName(TimeZoneNameProvider timeZoneNameProvider,
Locale locale, String requestID, int style, String tzid) {
String value = null;
switch (requestID) {
case "std":
value = timeZoneNameProvider.getDisplayName(tzid, false, style, locale);
break;
case "dst":
value = timeZoneNameProvider.getDisplayName(tzid, true, style, locale);
break;
case "generic":
value = timeZoneNameProvider.getGenericDisplayName(tzid, style, locale);
break;
}
return value;
}
}
// No instantiation
private TimeZoneNameUtility() {
}
}