mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-14 09:21:03 +00:00
ICU-8496 Merged #8774 Suppot locale category in com.ibm.icu.base plug-in (r30602) from trunk.
X-SVN-Rev: 30605
This commit is contained in:
parent
aa95e54bc0
commit
03c6cf40c1
22 changed files with 14953 additions and 10579 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
|||
/.project
|
||||
demos/out
|
||||
/doc
|
||||
eclipse-build/out
|
||||
main/classes/charset/out
|
||||
main/classes/collate/out
|
||||
main/classes/core/out
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
|
@ -1,180 +1,183 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.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 (o instanceof CaseInsensitiveKey) {
|
||||
return AsciiUtil.caseIgnoreMatch(_key, ((CaseInsensitiveKey)o)._key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return _hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
|
||||
public final class BaseLocale {
|
||||
|
||||
private static final boolean JDKIMPL = false;
|
||||
|
||||
public static final String SEP = "_";
|
||||
|
||||
private static final Cache CACHE = new Cache();
|
||||
public static final BaseLocale ROOT = BaseLocale.getInstance("", "", "", "");
|
||||
|
||||
private String _language = "";
|
||||
private String _script = "";
|
||||
private String _region = "";
|
||||
private String _variant = "";
|
||||
|
||||
private transient volatile int _hash = 0;
|
||||
|
||||
private BaseLocale(String language, String script, String region, String variant) {
|
||||
if (language != null) {
|
||||
_language = AsciiUtil.toLowerString(language).intern();
|
||||
}
|
||||
if (script != null) {
|
||||
_script = AsciiUtil.toTitleString(script).intern();
|
||||
}
|
||||
if (region != null) {
|
||||
_region = AsciiUtil.toUpperString(region).intern();
|
||||
}
|
||||
if (variant != null) {
|
||||
if (JDKIMPL) {
|
||||
// preserve upper/lower cases
|
||||
_variant = variant.intern();
|
||||
} else {
|
||||
_variant = AsciiUtil.toUpperString(variant).intern();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static BaseLocale getInstance(String language, String script, String region, String variant) {
|
||||
if (JDKIMPL) {
|
||||
// JDK uses deprecated ISO639.1 language codes for he, yi and id
|
||||
if (AsciiUtil.caseIgnoreMatch(language, "he")) {
|
||||
language = "iw";
|
||||
} else if (AsciiUtil.caseIgnoreMatch(language, "yi")) {
|
||||
language = "ji";
|
||||
} else if (AsciiUtil.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;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof BaseLocale)) {
|
||||
return false;
|
||||
}
|
||||
BaseLocale other = (BaseLocale)obj;
|
||||
return hashCode() == other.hashCode()
|
||||
&& _language.equals(other._language)
|
||||
&& _script.equals(other._script)
|
||||
&& _region.equals(other._region)
|
||||
&& _variant.equals(other._variant);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int h = _hash;
|
||||
if (h == 0) {
|
||||
// Generating a hash value from language, script, region and variant
|
||||
for (int i = 0; i < _language.length(); i++) {
|
||||
h = 31*h + _language.charAt(i);
|
||||
}
|
||||
for (int i = 0; i < _script.length(); i++) {
|
||||
h = 31*h + _script.charAt(i);
|
||||
}
|
||||
for (int i = 0; i < _region.length(); i++) {
|
||||
h = 31*h + _region.charAt(i);
|
||||
}
|
||||
for (int i = 0; i < _variant.length(); i++) {
|
||||
h = 31*h + _variant.charAt(i);
|
||||
}
|
||||
_hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
private static class Key implements Comparable<Key> {
|
||||
private String _lang = "";
|
||||
private String _scrt = "";
|
||||
private String _regn = "";
|
||||
private String _vart = "";
|
||||
|
||||
private volatile int _hash; // Default to 0
|
||||
|
||||
public Key(String language, String script, String region, String variant) {
|
||||
if (language != null) {
|
||||
_lang = language;
|
||||
}
|
||||
if (script != null) {
|
||||
_scrt = script;
|
||||
}
|
||||
if (region != null) {
|
||||
_regn = region;
|
||||
}
|
||||
if (variant != null) {
|
||||
_vart = variant;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (JDKIMPL) {
|
||||
return (this == obj) ||
|
||||
(obj instanceof Key)
|
||||
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._lang, this._lang)
|
||||
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._scrt, this._scrt)
|
||||
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._regn, this._regn)
|
||||
&& ((Key)obj)._vart.equals(_vart); // variant is case sensitive in JDK!
|
||||
}
|
||||
return (this == obj) ||
|
||||
(obj instanceof Key)
|
||||
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._lang, this._lang)
|
||||
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._scrt, this._scrt)
|
||||
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._regn, this._regn)
|
||||
&& AsciiUtil.caseIgnoreMatch(((Key)obj)._vart, this._vart);
|
||||
}
|
||||
|
||||
public int compareTo(Key other) {
|
||||
int res = AsciiUtil.caseIgnoreCompare(this._lang, other._lang);
|
||||
if (res == 0) {
|
||||
res = AsciiUtil.caseIgnoreCompare(this._scrt, other._scrt);
|
||||
if (res == 0) {
|
||||
res = AsciiUtil.caseIgnoreCompare(this._regn, other._regn);
|
||||
if (res == 0) {
|
||||
if (JDKIMPL) {
|
||||
res = this._vart.compareTo(other._vart);
|
||||
} else {
|
||||
res = AsciiUtil.caseIgnoreCompare(this._vart, other._vart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int h = _hash;
|
||||
if (h == 0) {
|
||||
// Generating a hash value from language, script, region and variant
|
||||
for (int i = 0; i < _lang.length(); i++) {
|
||||
h = 31*h + AsciiUtil.toLower(_lang.charAt(i));
|
||||
}
|
||||
for (int i = 0; i < _scrt.length(); i++) {
|
||||
h = 31*h + AsciiUtil.toLower(_scrt.charAt(i));
|
||||
}
|
||||
for (int i = 0; i < _regn.length(); i++) {
|
||||
h = 31*h + AsciiUtil.toLower(_regn.charAt(i));
|
||||
}
|
||||
for (int i = 0; i < _vart.length(); i++) {
|
||||
if (JDKIMPL) {
|
||||
h = 31*h + _vart.charAt(i);
|
||||
} else {
|
||||
h = 31*h + AsciiUtil.toLower(_vart.charAt(i));
|
||||
}
|
||||
}
|
||||
_hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
public static Key normalize(Key key) {
|
||||
String lang = AsciiUtil.toLowerString(key._lang).intern();
|
||||
String scrt = AsciiUtil.toTitleString(key._scrt).intern();
|
||||
String regn = AsciiUtil.toUpperString(key._regn).intern();
|
||||
String vart;
|
||||
if (JDKIMPL) {
|
||||
// preserve upper/lower cases
|
||||
vart = key._vart.intern();
|
||||
} else {
|
||||
vart = AsciiUtil.toUpperString(key._vart).intern();
|
||||
}
|
||||
return new Key(lang, scrt, regn, vart);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Cache extends LocaleObjectCache<Key, BaseLocale> {
|
||||
|
||||
public Cache() {
|
||||
}
|
||||
|
||||
protected Key normalizeKey(Key key) {
|
||||
return Key.normalize(key);
|
||||
}
|
||||
|
||||
protected BaseLocale createObject(Key key) {
|
||||
return new BaseLocale(key._lang, key._scrt, key._regn, key._vart);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
|
||||
public class Extension {
|
||||
private char _key;
|
||||
protected String _value;
|
||||
|
||||
protected Extension(char key) {
|
||||
_key = key;
|
||||
}
|
||||
|
||||
Extension(char key, String value) {
|
||||
_key = key;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public char getKey() {
|
||||
return _key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
public String getID() {
|
||||
return _key + LanguageTag.SEP + _value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getID();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,684 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class InternalLocaleBuilder {
|
||||
|
||||
private static final boolean JDKIMPL = false;
|
||||
|
||||
private String _language = "";
|
||||
private String _script = "";
|
||||
private String _region = "";
|
||||
private String _variant = "";
|
||||
|
||||
private static final CaseInsensitiveChar PRIVUSE_KEY = new CaseInsensitiveChar(LanguageTag.PRIVATEUSE.charAt(0));
|
||||
|
||||
private HashMap<CaseInsensitiveChar, String> _extensions;
|
||||
private HashSet<CaseInsensitiveString> _uattributes;
|
||||
private HashMap<CaseInsensitiveString, String> _ukeywords;
|
||||
|
||||
|
||||
public InternalLocaleBuilder() {
|
||||
}
|
||||
|
||||
public InternalLocaleBuilder setLanguage(String language) throws LocaleSyntaxException {
|
||||
if (language == null || language.length() == 0) {
|
||||
_language = "";
|
||||
} else {
|
||||
if (!LanguageTag.isLanguage(language)) {
|
||||
throw new LocaleSyntaxException("Ill-formed language: " + language, 0);
|
||||
}
|
||||
_language = language;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public InternalLocaleBuilder setScript(String script) throws LocaleSyntaxException {
|
||||
if (script == null || script.length() == 0) {
|
||||
_script = "";
|
||||
} else {
|
||||
if (!LanguageTag.isScript(script)) {
|
||||
throw new LocaleSyntaxException("Ill-formed script: " + script, 0);
|
||||
}
|
||||
_script = script;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public InternalLocaleBuilder setRegion(String region) throws LocaleSyntaxException {
|
||||
if (region == null || region.length() == 0) {
|
||||
_region = "";
|
||||
} else {
|
||||
if (!LanguageTag.isRegion(region)) {
|
||||
throw new LocaleSyntaxException("Ill-formed region: " + region, 0);
|
||||
}
|
||||
_region = region;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public InternalLocaleBuilder setVariant(String variant) throws LocaleSyntaxException {
|
||||
if (variant == null || variant.length() == 0) {
|
||||
_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);
|
||||
}
|
||||
_variant = var;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public InternalLocaleBuilder addUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException {
|
||||
if (attribute == null || !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<CaseInsensitiveString>(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<CaseInsensitiveString, String>(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 = (value == null || value.length() == 0);
|
||||
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<CaseInsensitiveChar, String>(4);
|
||||
}
|
||||
_extensions.put(key, val);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set extension/private subtags in a single string representation
|
||||
*/
|
||||
public InternalLocaleBuilder setExtensions(String subtags) throws LocaleSyntaxException {
|
||||
if (subtags == null || subtags.length() == 0) {
|
||||
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<String>(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 (bcpExtensions != null && bcpExtensions.size() > 0) {
|
||||
HashSet<CaseInsensitiveChar> processedExtensions = new HashSet<CaseInsensitiveChar>(bcpExtensions.size());
|
||||
for (String bcpExt : bcpExtensions) {
|
||||
CaseInsensitiveChar key = new CaseInsensitiveChar(bcpExt.charAt(0));
|
||||
// ignore duplicates
|
||||
if (!processedExtensions.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<CaseInsensitiveChar, String>(4);
|
||||
}
|
||||
_extensions.put(key, bcpExt.substring(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (privateuse != null && privateuse.length() > 0) {
|
||||
// privateuse string contains prefix, e.g. "x-abc-def"
|
||||
if (_extensions == null) {
|
||||
_extensions = new HashMap<CaseInsensitiveChar, String>(1);
|
||||
}
|
||||
_extensions.put(new CaseInsensitiveChar(privateuse.charAt(0)), privateuse.substring(2));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset Builder's internal state with the given language tag
|
||||
*/
|
||||
public InternalLocaleBuilder setLanguageTag(LanguageTag langtag) {
|
||||
clear();
|
||||
if (langtag.getExtlangs().size() > 0) {
|
||||
_language = langtag.getExtlangs().get(0);
|
||||
} else {
|
||||
String language = langtag.getLanguage();
|
||||
if (!language.equals(LanguageTag.UNDETERMINED)) {
|
||||
_language = language;
|
||||
}
|
||||
}
|
||||
_script = langtag.getScript();
|
||||
_region = langtag.getRegion();
|
||||
|
||||
List<String> bcpVariants = langtag.getVariants();
|
||||
if (bcpVariants.size() > 0) {
|
||||
StringBuilder var = new StringBuilder(bcpVariants.get(0));
|
||||
for (int i = 1; i < bcpVariants.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 extensions) throws LocaleSyntaxException {
|
||||
String language = base.getLanguage();
|
||||
String script = base.getScript();
|
||||
String region = base.getRegion();
|
||||
String variant = base.getVariant();
|
||||
|
||||
if (JDKIMPL) {
|
||||
// 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(extensions.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(extensions.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.
|
||||
_language = language;
|
||||
_script = script;
|
||||
_region = region;
|
||||
_variant = variant;
|
||||
clearExtensions();
|
||||
|
||||
Set<Character> extKeys = (extensions == null) ? null : extensions.getKeys();
|
||||
if (extKeys != null) {
|
||||
// map extensions back to builder's internal format
|
||||
for (Character key : extKeys) {
|
||||
Extension e = extensions.getExtension(key);
|
||||
if (e instanceof UnicodeLocaleExtension) {
|
||||
UnicodeLocaleExtension ue = (UnicodeLocaleExtension)e;
|
||||
for (String uatr : ue.getUnicodeLocaleAttributes()) {
|
||||
if (_uattributes == null) {
|
||||
_uattributes = new HashSet<CaseInsensitiveString>(4);
|
||||
}
|
||||
_uattributes.add(new CaseInsensitiveString(uatr));
|
||||
}
|
||||
for (String ukey : ue.getUnicodeLocaleKeys()) {
|
||||
if (_ukeywords == null) {
|
||||
_ukeywords = new HashMap<CaseInsensitiveString, String>(4);
|
||||
}
|
||||
_ukeywords.put(new CaseInsensitiveString(ukey), ue.getUnicodeLocaleType(ukey));
|
||||
}
|
||||
} else {
|
||||
if (_extensions == null) {
|
||||
_extensions = new HashMap<CaseInsensitiveChar, String>(4);
|
||||
}
|
||||
_extensions.put(new CaseInsensitiveChar(key.charValue()), 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 = _language;
|
||||
String script = _script;
|
||||
String region = _region;
|
||||
String variant = _variant;
|
||||
|
||||
// Special private use subtag sequence identified by "lvariant" will be
|
||||
// interpreted as Java variant.
|
||||
if (_extensions != null) {
|
||||
String privuse = _extensions.get(PRIVUSE_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 (AsciiUtil.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 ((_extensions == null || _extensions.size() == 0)
|
||||
&& (_uattributes == null || _uattributes.size() == 0)
|
||||
&& (_ukeywords == null || _ukeywords.size() == 0)) {
|
||||
return LocaleExtensions.EMPTY_EXTENSIONS;
|
||||
}
|
||||
|
||||
return new LocaleExtensions(_extensions, _uattributes, _ukeywords);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (AsciiUtil.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<CaseInsensitiveString>(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<CaseInsensitiveString, String>(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<CaseInsensitiveString, String>(4);
|
||||
}
|
||||
_ukeywords.put(key, type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
itr.next();
|
||||
}
|
||||
}
|
||||
|
||||
static class CaseInsensitiveString {
|
||||
private String _s;
|
||||
|
||||
CaseInsensitiveString(String s) {
|
||||
_s = s;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return _s;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return AsciiUtil.toLowerString(_s).hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CaseInsensitiveString)) {
|
||||
return false;
|
||||
}
|
||||
return AsciiUtil.caseIgnoreMatch(_s, ((CaseInsensitiveString)obj).value());
|
||||
}
|
||||
}
|
||||
|
||||
static class CaseInsensitiveChar {
|
||||
private char _c;
|
||||
|
||||
CaseInsensitiveChar(char c) {
|
||||
_c = c;
|
||||
}
|
||||
|
||||
public char value() {
|
||||
return _c;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return AsciiUtil.toLower(_c);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CaseInsensitiveChar)) {
|
||||
return false;
|
||||
}
|
||||
return _c == AsciiUtil.toLower(((CaseInsensitiveChar)obj).value());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,720 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2010-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.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 {
|
||||
private static final boolean JDKIMPL = false;
|
||||
|
||||
//
|
||||
// static fields
|
||||
//
|
||||
public static final String SEP = "-";
|
||||
public static final String PRIVATEUSE = "x";
|
||||
public static 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
|
||||
private static final Map<AsciiUtil.CaseInsensitiveKey, String[]> GRANDFATHERED =
|
||||
new HashMap<AsciiUtil.CaseInsensitiveKey, String[]>();
|
||||
|
||||
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(new AsciiUtil.CaseInsensitiveKey(e[0]), e);
|
||||
}
|
||||
}
|
||||
|
||||
private LanguageTag() {
|
||||
}
|
||||
|
||||
/*
|
||||
* BNF in RFC5464
|
||||
*
|
||||
* 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(new AsciiUtil.CaseInsensitiveKey(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<String>(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<String>(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.size() == 0) {
|
||||
_extensions = new ArrayList<String>(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 (language.length() > 0 && isLanguage(language)) {
|
||||
// Convert a deprecated language code used by Java to
|
||||
// a 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 (script.length() > 0 && isScript(script)) {
|
||||
tag._script = canonicalizeScript(script);
|
||||
hasSubtag = true;
|
||||
}
|
||||
|
||||
if (region.length() > 0 && isRegion(region)) {
|
||||
tag._region = canonicalizeRegion(region);
|
||||
hasSubtag = true;
|
||||
}
|
||||
|
||||
if (JDKIMPL) {
|
||||
// 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<String>();
|
||||
}
|
||||
if (JDKIMPL) {
|
||||
variants.add(var); // Do not canonicalize!
|
||||
} else {
|
||||
variants.add(canonicalizeVariant(var));
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (!JDKIMPL) {
|
||||
prvv = AsciiUtil.toLowerString(prvv);
|
||||
}
|
||||
buf.append(prvv);
|
||||
varitr.next();
|
||||
}
|
||||
if (buf.length() > 0) {
|
||||
privuseVar = buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> extensions = null;
|
||||
String privateuse = null;
|
||||
|
||||
Set<Character> locextKeys = localeExtensions.getKeys();
|
||||
for (Character locextKey : locextKeys) {
|
||||
Extension ext = localeExtensions.getExtension(locextKey);
|
||||
if (isPrivateusePrefixChar(locextKey.charValue())) {
|
||||
privateuse = ext.getValue();
|
||||
} else {
|
||||
if (extensions == null) {
|
||||
extensions = new ArrayList<String>();
|
||||
}
|
||||
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() {
|
||||
return Collections.unmodifiableList(_extlangs);
|
||||
}
|
||||
|
||||
public String getScript() {
|
||||
return _script;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return _region;
|
||||
}
|
||||
|
||||
public List<String> getVariants() {
|
||||
return Collections.unmodifiableList(_variants);
|
||||
}
|
||||
|
||||
public List<String> getExtensions() {
|
||||
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
|
||||
return (s.length() >= 2) && (s.length() <= 8) && AsciiUtil.isAlphaString(s);
|
||||
}
|
||||
|
||||
public static boolean isExtlang(String s) {
|
||||
// extlang = 3ALPHA ; selected ISO 639 codes
|
||||
// *2("-" 3ALPHA) ; permanently reserved
|
||||
return (s.length() == 3) && AsciiUtil.isAlphaString(s);
|
||||
}
|
||||
|
||||
public static boolean isScript(String s) {
|
||||
// script = 4ALPHA ; ISO 15924 code
|
||||
return (s.length() == 4) && AsciiUtil.isAlphaString(s);
|
||||
}
|
||||
|
||||
public static boolean isRegion(String s) {
|
||||
// region = 2ALPHA ; ISO 3166-1 code
|
||||
// / 3DIGIT ; UN M.49 code
|
||||
return ((s.length() == 2) && AsciiUtil.isAlphaString(s))
|
||||
|| ((s.length() == 3) && AsciiUtil.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 AsciiUtil.isAlphaNumericString(s);
|
||||
}
|
||||
if (len == 4) {
|
||||
return AsciiUtil.isNumeric(s.charAt(0))
|
||||
&& AsciiUtil.isAlphaNumeric(s.charAt(1))
|
||||
&& AsciiUtil.isAlphaNumeric(s.charAt(2))
|
||||
&& AsciiUtil.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)
|
||||
&& AsciiUtil.isAlphaString(s)
|
||||
&& !AsciiUtil.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))
|
||||
return (s.length() >= 2) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
|
||||
}
|
||||
|
||||
public static boolean isPrivateusePrefix(String s) {
|
||||
// privateuse = "x" 1*("-" (1*8alphanum))
|
||||
return (s.length() == 1)
|
||||
&& AsciiUtil.caseIgnoreMatch(PRIVATEUSE, s);
|
||||
}
|
||||
|
||||
public static boolean isPrivateusePrefixChar(char c) {
|
||||
return (AsciiUtil.caseIgnoreMatch(PRIVATEUSE, String.valueOf(c)));
|
||||
}
|
||||
|
||||
public static boolean isPrivateuseSubtag(String s) {
|
||||
// privateuse = "x" 1*("-" (1*8alphanum))
|
||||
return (s.length() >= 1) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
|
||||
}
|
||||
|
||||
//
|
||||
// Language subtag canonicalization methods
|
||||
//
|
||||
|
||||
public static String canonicalizeLanguage(String s) {
|
||||
return AsciiUtil.toLowerString(s);
|
||||
}
|
||||
|
||||
public static String canonicalizeExtlang(String s) {
|
||||
return AsciiUtil.toLowerString(s);
|
||||
}
|
||||
|
||||
public static String canonicalizeScript(String s) {
|
||||
return AsciiUtil.toTitleString(s);
|
||||
}
|
||||
|
||||
public static String canonicalizeRegion(String s) {
|
||||
return AsciiUtil.toUpperString(s);
|
||||
}
|
||||
|
||||
public static String canonicalizeVariant(String s) {
|
||||
return AsciiUtil.toLowerString(s);
|
||||
}
|
||||
|
||||
public static String canonicalizeExtension(String s) {
|
||||
return AsciiUtil.toLowerString(s);
|
||||
}
|
||||
|
||||
public static String canonicalizeExtensionSingleton(String s) {
|
||||
return AsciiUtil.toLowerString(s);
|
||||
}
|
||||
|
||||
public static String canonicalizeExtensionSubtag(String s) {
|
||||
return AsciiUtil.toLowerString(s);
|
||||
}
|
||||
|
||||
public static String canonicalizePrivateuse(String s) {
|
||||
return AsciiUtil.toLowerString(s);
|
||||
}
|
||||
|
||||
public static String canonicalizePrivateuseSubtag(String s) {
|
||||
return AsciiUtil.toLowerString(s);
|
||||
}
|
||||
|
||||
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 : _extlangs) {
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.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.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.ibm.icu.impl.locale.InternalLocaleBuilder.CaseInsensitiveChar;
|
||||
import com.ibm.icu.impl.locale.InternalLocaleBuilder.CaseInsensitiveString;
|
||||
|
||||
|
||||
public class LocaleExtensions {
|
||||
|
||||
private SortedMap<Character, Extension> _map;
|
||||
private String _id;
|
||||
|
||||
private static final SortedMap<Character, Extension> EMPTY_MAP =
|
||||
Collections.unmodifiableSortedMap(new TreeMap<Character, Extension>());
|
||||
|
||||
public static final LocaleExtensions EMPTY_EXTENSIONS;
|
||||
public static final LocaleExtensions CALENDAR_JAPANESE;
|
||||
public static final LocaleExtensions NUMBER_THAI;
|
||||
|
||||
static {
|
||||
EMPTY_EXTENSIONS = new LocaleExtensions();
|
||||
EMPTY_EXTENSIONS._id = "";
|
||||
EMPTY_EXTENSIONS._map = EMPTY_MAP;
|
||||
|
||||
CALENDAR_JAPANESE = new LocaleExtensions();
|
||||
CALENDAR_JAPANESE._id = "u-ca-japanese";
|
||||
CALENDAR_JAPANESE._map = new TreeMap<Character, Extension>();
|
||||
CALENDAR_JAPANESE._map.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), UnicodeLocaleExtension.CA_JAPANESE);
|
||||
|
||||
NUMBER_THAI = new LocaleExtensions();
|
||||
NUMBER_THAI._id = "u-nu-thai";
|
||||
NUMBER_THAI._map = new TreeMap<Character, Extension>();
|
||||
NUMBER_THAI._map.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), UnicodeLocaleExtension.NU_THAI);
|
||||
}
|
||||
|
||||
private LocaleExtensions() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Package local constructor, only used by InternalLocaleBuilder.
|
||||
*/
|
||||
LocaleExtensions(Map<CaseInsensitiveChar, String> extensions,
|
||||
Set<CaseInsensitiveString> uattributes, Map<CaseInsensitiveString, String> ukeywords) {
|
||||
boolean hasExtension = (extensions != null && extensions.size() > 0);
|
||||
boolean hasUAttributes = (uattributes != null && uattributes.size() > 0);
|
||||
boolean hasUKeywords = (ukeywords != null && ukeywords.size() > 0);
|
||||
|
||||
if (!hasExtension && !hasUAttributes && !hasUKeywords) {
|
||||
_map = EMPTY_MAP;
|
||||
_id = "";
|
||||
return;
|
||||
}
|
||||
|
||||
// Build extension map
|
||||
_map = new TreeMap<Character, Extension>();
|
||||
if (hasExtension) {
|
||||
for (Entry<CaseInsensitiveChar, String> ext : extensions.entrySet()) {
|
||||
char key = AsciiUtil.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;
|
||||
}
|
||||
}
|
||||
|
||||
Extension e = new Extension(key, AsciiUtil.toLowerString(value));
|
||||
_map.put(Character.valueOf(key), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUAttributes || hasUKeywords) {
|
||||
TreeSet<String> uaset = null;
|
||||
TreeMap<String, String> ukmap = null;
|
||||
|
||||
if (hasUAttributes) {
|
||||
uaset = new TreeSet<String>();
|
||||
for (CaseInsensitiveString cis : uattributes) {
|
||||
uaset.add(AsciiUtil.toLowerString(cis.value()));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUKeywords) {
|
||||
ukmap = new TreeMap<String, String>();
|
||||
for (Entry<CaseInsensitiveString, String> kwd : ukeywords.entrySet()) {
|
||||
String key = AsciiUtil.toLowerString(kwd.getKey().value());
|
||||
String type = AsciiUtil.toLowerString(kwd.getValue());
|
||||
ukmap.put(key, type);
|
||||
}
|
||||
}
|
||||
|
||||
UnicodeLocaleExtension ule = new UnicodeLocaleExtension(uaset, ukmap);
|
||||
_map.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), ule);
|
||||
}
|
||||
|
||||
if (_map.size() == 0) {
|
||||
// this could happen when only privuateuse with special variant
|
||||
_map = EMPTY_MAP;
|
||||
_id = "";
|
||||
} else {
|
||||
_id = toID(_map);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Character> getKeys() {
|
||||
return Collections.unmodifiableSet(_map.keySet());
|
||||
}
|
||||
|
||||
public Extension getExtension(Character key) {
|
||||
return _map.get(Character.valueOf(AsciiUtil.toLower(key.charValue())));
|
||||
}
|
||||
|
||||
public String getExtensionValue(Character key) {
|
||||
Extension ext = _map.get(Character.valueOf(AsciiUtil.toLower(key.charValue())));
|
||||
if (ext == null) {
|
||||
return null;
|
||||
}
|
||||
return ext.getValue();
|
||||
}
|
||||
|
||||
public Set<String> getUnicodeLocaleAttributes() {
|
||||
Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
|
||||
if (ext == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
assert (ext instanceof UnicodeLocaleExtension);
|
||||
return ((UnicodeLocaleExtension)ext).getUnicodeLocaleAttributes();
|
||||
}
|
||||
|
||||
public Set<String> getUnicodeLocaleKeys() {
|
||||
Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
|
||||
if (ext == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
assert (ext instanceof UnicodeLocaleExtension);
|
||||
return ((UnicodeLocaleExtension)ext).getUnicodeLocaleKeys();
|
||||
}
|
||||
|
||||
public String getUnicodeLocaleType(String unicodeLocaleKey) {
|
||||
Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
|
||||
if (ext == null) {
|
||||
return null;
|
||||
}
|
||||
assert (ext instanceof UnicodeLocaleExtension);
|
||||
return ((UnicodeLocaleExtension)ext).getUnicodeLocaleType(AsciiUtil.toLowerString(unicodeLocaleKey));
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return _map.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().charValue();
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
public String getID() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return _id.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof LocaleExtensions)) {
|
||||
return false;
|
||||
}
|
||||
return this._id.equals(((LocaleExtensions)other)._id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public abstract class LocaleObjectCache<K, V> {
|
||||
private ConcurrentHashMap<K, CacheEntry<K, V>> _map;
|
||||
private ReferenceQueue<V> _queue = new ReferenceQueue<V>();
|
||||
|
||||
public LocaleObjectCache() {
|
||||
this(16, 0.75f, 16);
|
||||
}
|
||||
|
||||
public LocaleObjectCache(int initialCapacity, float loadFactor, int concurrencyLevel) {
|
||||
_map = new ConcurrentHashMap<K, CacheEntry<K, V>>(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) {
|
||||
key = normalizeKey(key);
|
||||
V newVal = createObject(key);
|
||||
if (key == null || newVal == null) {
|
||||
// subclass must return non-null key/value object
|
||||
return null;
|
||||
}
|
||||
|
||||
CacheEntry<K, V> newEntry = new CacheEntry<K, V>(key, newVal, _queue);
|
||||
|
||||
while (value == null) {
|
||||
cleanStaleEntries();
|
||||
entry = _map.putIfAbsent(key, newEntry);
|
||||
if (entry == null) {
|
||||
value = newVal;
|
||||
break;
|
||||
} else {
|
||||
value = entry.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@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);
|
||||
_key = key;
|
||||
}
|
||||
|
||||
K getKey() {
|
||||
return _key;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2010-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
public class ParseStatus {
|
||||
int _parseLength = 0;
|
||||
int _errorIndex = -1;
|
||||
String _errorMsg = null;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
public class StringTokenIterator {
|
||||
private String _text;
|
||||
private String _dlms;
|
||||
|
||||
private String _token;
|
||||
private int _start;
|
||||
private int _end;
|
||||
private boolean _done;
|
||||
|
||||
public StringTokenIterator(String text, String dlms) {
|
||||
_text = text;
|
||||
_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) {
|
||||
_text = text;
|
||||
setStart(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
private int nextDelimiter(int start) {
|
||||
int idx = start;
|
||||
outer: while (idx < _text.length()) {
|
||||
char c = _text.charAt(idx);
|
||||
for (int i = 0; i < _dlms.length(); i++) {
|
||||
if (c == _dlms.charAt(i)) {
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
import java.util.Collections;
|
||||
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;
|
||||
|
||||
public class UnicodeLocaleExtension extends Extension {
|
||||
public static final char SINGLETON = 'u';
|
||||
|
||||
private static final SortedSet<String> EMPTY_SORTED_SET = new TreeSet<String>();
|
||||
private static final SortedMap<String, String> EMPTY_SORTED_MAP = new TreeMap<String, String>();
|
||||
|
||||
private SortedSet<String> _attributes = EMPTY_SORTED_SET;
|
||||
private SortedMap<String, String> _keywords = EMPTY_SORTED_MAP;
|
||||
|
||||
public static final UnicodeLocaleExtension CA_JAPANESE;
|
||||
public static final UnicodeLocaleExtension NU_THAI;
|
||||
|
||||
static {
|
||||
CA_JAPANESE = new UnicodeLocaleExtension();
|
||||
CA_JAPANESE._keywords = new TreeMap<String, String>();
|
||||
CA_JAPANESE._keywords.put("ca", "japanese");
|
||||
CA_JAPANESE._value = "ca-japanese";
|
||||
|
||||
NU_THAI = new UnicodeLocaleExtension();
|
||||
NU_THAI._keywords = new TreeMap<String, String>();
|
||||
NU_THAI._keywords.put("nu", "thai");
|
||||
NU_THAI._value = "nu-thai";
|
||||
}
|
||||
|
||||
private UnicodeLocaleExtension() {
|
||||
super(SINGLETON);
|
||||
}
|
||||
|
||||
UnicodeLocaleExtension(SortedSet<String> attributes, SortedMap<String, String> keywords) {
|
||||
this();
|
||||
if (attributes != null && attributes.size() > 0) {
|
||||
_attributes = attributes;
|
||||
}
|
||||
if (keywords != null && keywords.size() > 0) {
|
||||
_keywords = keywords;
|
||||
}
|
||||
|
||||
if (_attributes.size() > 0 || _keywords.size() > 0) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String attribute : _attributes) {
|
||||
sb.append(LanguageTag.SEP).append(attribute);
|
||||
}
|
||||
for (Entry<String, String> keyword : _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);
|
||||
}
|
||||
}
|
||||
_value = sb.substring(1); // skip leading '-'
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getUnicodeLocaleAttributes() {
|
||||
return Collections.unmodifiableSet(_attributes);
|
||||
}
|
||||
|
||||
public Set<String> getUnicodeLocaleKeys() {
|
||||
return Collections.unmodifiableSet(_keywords.keySet());
|
||||
}
|
||||
|
||||
public String getUnicodeLocaleType(String unicodeLocaleKey) {
|
||||
return _keywords.get(unicodeLocaleKey);
|
||||
}
|
||||
|
||||
public static boolean isSingletonChar(char c) {
|
||||
return (SINGLETON == AsciiUtil.toLower(c));
|
||||
}
|
||||
|
||||
public static boolean isAttribute(String s) {
|
||||
// 3*8alphanum
|
||||
return (s.length() >= 3) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
|
||||
}
|
||||
|
||||
public static boolean isKey(String s) {
|
||||
// 2alphanum
|
||||
return (s.length() == 2) && AsciiUtil.isAlphaNumericString(s);
|
||||
}
|
||||
|
||||
public static boolean isTypeSubtag(String s) {
|
||||
// 3*8alphanum
|
||||
return (s.length() >= 3) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.util;
|
||||
|
||||
/**
|
||||
* Thrown by methods in {@link ULocale} and {@link ULocale.Builder} to
|
||||
* indicate that an argument is not a well-formed BCP 47 tag.
|
||||
*
|
||||
* @see ULocale
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public class IllformedLocaleException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int _errIdx = -1;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>IllformedLocaleException</code> with no
|
||||
* detail message and -1 as the error index.
|
||||
* @draft ICU 4.6
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public IllformedLocaleException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>IllformedLocaleException</code> with the
|
||||
* given message and -1 as the error index.
|
||||
*
|
||||
* @param message the message
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public IllformedLocaleException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>IllformedLocaleException</code> with the
|
||||
* given message and the error index. The error index is the approximate
|
||||
* offset from the start of the ill-formed value to the point where the
|
||||
* parse first detected an error. A negative error index value indicates
|
||||
* either the error index is not applicable or unknown.
|
||||
*
|
||||
* @param message the message
|
||||
* @param errorIndex the index
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public IllformedLocaleException(String message, int errorIndex) {
|
||||
super(message + ((errorIndex < 0) ? "" : " [at index " + errorIndex + "]"));
|
||||
_errIdx = errorIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index where the error was found. A negative value indicates
|
||||
* either the error index is not applicable or unknown.
|
||||
*
|
||||
* @return the error index
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public int getErrorIndex() {
|
||||
return _errIdx;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
Loading…
Add table
Reference in a new issue