ICU-6635 Updated the implementation of BCP47 language tag support, locale builder, etc to match the latest JDK locale enhancement proposal.
X-SVN-Rev: 26804
This commit is contained in:
parent
e698d415eb
commit
e5dc053c11
@ -86,6 +86,9 @@ public final class AsciiUtil {
|
||||
}
|
||||
|
||||
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')) {
|
||||
|
@ -8,64 +8,60 @@
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
|
||||
|
||||
public final class BaseLocale {
|
||||
|
||||
private static final boolean JDKIMPL = false;
|
||||
|
||||
private String _language = "";
|
||||
private String _script = "";
|
||||
private String _region = "";
|
||||
private String _variant = "";
|
||||
|
||||
private transient String _id = "";
|
||||
private transient String _java6string = "";
|
||||
private transient BaseLocale _parent;
|
||||
private static final LocaleObjectCache<Key, BaseLocale> BASELOCALE_CACHE
|
||||
= new LocaleObjectCache<Key, BaseLocale>();
|
||||
|
||||
private static final char SEPCHAR = '_';
|
||||
|
||||
private static final LocaleObjectCache<BaseLocaleKey,BaseLocale> BASELOCALECACHE
|
||||
= new LocaleObjectCache<BaseLocaleKey,BaseLocale>();
|
||||
|
||||
public static final BaseLocale ROOT = new BaseLocale("", "", "", "");
|
||||
public static final BaseLocale ROOT = BaseLocale.getInstance("", "", "", "");
|
||||
|
||||
private BaseLocale(String language, String script, String region, String variant) {
|
||||
if (language != null) {
|
||||
_language = language;
|
||||
_language = AsciiUtil.toLowerString(language).intern();
|
||||
}
|
||||
if (script != null) {
|
||||
_script = script;
|
||||
_script = AsciiUtil.toTitleString(script).intern();
|
||||
}
|
||||
if (region != null) {
|
||||
_region = region;
|
||||
_region = AsciiUtil.toUpperString(region).intern();
|
||||
}
|
||||
if (variant != null) {
|
||||
_variant = variant;
|
||||
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) {
|
||||
BaseLocaleKey key = new BaseLocaleKey(language, script, region, variant);
|
||||
BaseLocale baseLocale = BASELOCALECACHE.get(key);
|
||||
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 = BASELOCALE_CACHE.get(key);
|
||||
if (baseLocale == null) {
|
||||
// Create a canonical BaseLocale instance
|
||||
baseLocale = new BaseLocale(language, script, region, variant).canonicalize();
|
||||
BASELOCALECACHE.put(baseLocale.createKey(), baseLocale);
|
||||
baseLocale = new BaseLocale(language, script, region, variant);
|
||||
BASELOCALE_CACHE.put(baseLocale.createKey(), baseLocale);
|
||||
}
|
||||
return baseLocale;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return (this == obj) ||
|
||||
((obj instanceof BaseLocale) && _id == (((BaseLocale)obj)._id));
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return _id.hashCode();
|
||||
}
|
||||
|
||||
public String getJava6String() {
|
||||
return _java6string;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return _language;
|
||||
}
|
||||
@ -82,116 +78,41 @@ public final class BaseLocale {
|
||||
return _variant;
|
||||
}
|
||||
|
||||
public BaseLocale getParent() {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
public String getID() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return _id;
|
||||
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();
|
||||
}
|
||||
|
||||
private BaseLocale canonicalize() {
|
||||
|
||||
StringBuilder id = new StringBuilder();
|
||||
|
||||
int languageLen = _language.length();
|
||||
int scriptLen = _script.length();
|
||||
int regionLen = _region.length();
|
||||
int variantLen = _variant.length();
|
||||
|
||||
if (languageLen > 0) {
|
||||
// language to lower case
|
||||
_language = AsciiUtil.toLowerString(_language).intern();
|
||||
|
||||
id.append(_language);
|
||||
}
|
||||
|
||||
if (scriptLen > 0) {
|
||||
// script - the first letter to upper case, the rest to lower case
|
||||
_script = AsciiUtil.toTitleString(_script).intern();
|
||||
|
||||
id.append(SEPCHAR);
|
||||
id.append(_script);
|
||||
}
|
||||
|
||||
if (regionLen > 0) {
|
||||
// region to upper case
|
||||
_region = AsciiUtil.toUpperString(_region).intern();
|
||||
|
||||
id.append(SEPCHAR);
|
||||
id.append(_region);
|
||||
}
|
||||
|
||||
if (variantLen > 0) {
|
||||
// variant in JDK conventionally use upper case letters,
|
||||
// but it was actually case sensitive. We normalize
|
||||
// variant to upper case here, which might be breaking
|
||||
// change. But at least it works well for all variants
|
||||
// defined by JDK.
|
||||
_variant = AsciiUtil.toUpperString(_variant).intern();
|
||||
|
||||
if (regionLen == 0) {
|
||||
id.append(SEPCHAR);
|
||||
}
|
||||
id.append(SEPCHAR);
|
||||
id.append(_variant);
|
||||
}
|
||||
|
||||
_id = id.toString().intern();
|
||||
|
||||
// Compose legacy JDK ID string if required
|
||||
if (languageLen == 0 && regionLen == 0 && variantLen > 0) {
|
||||
_java6string = "";
|
||||
} else if (scriptLen > 0 || (regionLen == 0 && variantLen > 0)) {
|
||||
StringBuilder buf = new StringBuilder(_language);
|
||||
if (regionLen > 0) {
|
||||
buf.append(SEPCHAR);
|
||||
buf.append(_region);
|
||||
} else if (variantLen > 0) {
|
||||
buf.append(SEPCHAR);
|
||||
}
|
||||
if (variantLen > 0) {
|
||||
buf.append(SEPCHAR);
|
||||
buf.append(_variant);
|
||||
}
|
||||
_java6string = buf.toString().intern();
|
||||
} else {
|
||||
_java6string = _id;
|
||||
}
|
||||
|
||||
// Resolve parent
|
||||
if (variantLen > 0) {
|
||||
// variant field in Java Locale may contain multiple
|
||||
// subtags
|
||||
int lastSep = _variant.lastIndexOf(SEPCHAR);
|
||||
if (lastSep == -1) {
|
||||
_parent = getInstance(_language, _script, _region, "");
|
||||
} else {
|
||||
_parent = getInstance(_language, _script, _region, _variant.substring(0, lastSep));
|
||||
}
|
||||
} else if (regionLen > 0) {
|
||||
_parent = getInstance(_language, _script, "", "");
|
||||
} else if (scriptLen > 0) {
|
||||
_parent = getInstance(_language, "", "", "");
|
||||
} else if (languageLen > 0) {
|
||||
_parent = ROOT;
|
||||
} else {
|
||||
// This is the root
|
||||
// We should never get here, because ROOT is pre-populated.
|
||||
_parent = null;
|
||||
}
|
||||
return this;
|
||||
private Key createKey() {
|
||||
return new Key(_language, _script, _region, _variant);
|
||||
}
|
||||
|
||||
private BaseLocaleKey createKey() {
|
||||
return new BaseLocaleKey(_language, _script, _region, _variant);
|
||||
}
|
||||
|
||||
public static class BaseLocaleKey implements Comparable<BaseLocaleKey> {
|
||||
private static class Key implements Comparable<Key> {
|
||||
private String _lang = "";
|
||||
private String _scrt = "";
|
||||
private String _regn = "";
|
||||
@ -199,7 +120,7 @@ public final class BaseLocale {
|
||||
|
||||
private int _hash; // Default to 0
|
||||
|
||||
public BaseLocaleKey(String language, String script, String region, String variant) {
|
||||
public Key(String language, String script, String region, String variant) {
|
||||
if (language != null) {
|
||||
_lang = language;
|
||||
}
|
||||
@ -215,22 +136,34 @@ public final class BaseLocale {
|
||||
}
|
||||
|
||||
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 BaseLocaleKey)
|
||||
&& AsciiUtil.caseIgnoreMatch(((BaseLocaleKey)obj)._lang, this._lang)
|
||||
&& AsciiUtil.caseIgnoreMatch(((BaseLocaleKey)obj)._scrt, this._scrt)
|
||||
&& AsciiUtil.caseIgnoreMatch(((BaseLocaleKey)obj)._regn, this._regn)
|
||||
&& ((BaseLocaleKey)obj)._vart.equals(_vart); // variant is case sensitive in JDK!
|
||||
(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(BaseLocaleKey other) {
|
||||
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) {
|
||||
res = AsciiUtil.caseIgnoreCompare(this._vart, other._vart);
|
||||
if (JDKIMPL) {
|
||||
res = this._vart.compareTo(other._vart);
|
||||
} else {
|
||||
res = AsciiUtil.caseIgnoreCompare(this._vart, other._vart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -251,7 +184,11 @@ public final class BaseLocale {
|
||||
h = 31*h + AsciiUtil.toLower(_regn.charAt(i));
|
||||
}
|
||||
for (int i = 0; i < _vart.length(); i++) {
|
||||
h = 31*h + AsciiUtil.toLower(_vart.charAt(i));
|
||||
if (JDKIMPL) {
|
||||
h = 31*h + _vart.charAt(i);
|
||||
} else {
|
||||
h = 31*h + AsciiUtil.toLower(_vart.charAt(i));
|
||||
}
|
||||
}
|
||||
_hash = h;
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
|
||||
|
||||
public class Extension {
|
||||
private char _key;
|
||||
protected String _value;
|
||||
|
||||
protected Extension(char key) {
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public char getKey() {
|
||||
return _key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
public static Extension create(StringTokenIterator itr, ParseStatus sts) {
|
||||
if (sts.isError() || itr.isDone()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Extension ext = null;
|
||||
String key = itr.current();
|
||||
if (LanguageTag.isExtensionSingleton(key) || LanguageTag.isPrivateuseSingleton(key)) {
|
||||
itr.next();
|
||||
ext = create(key.charAt(0), itr, sts);
|
||||
}
|
||||
|
||||
return ext;
|
||||
}
|
||||
|
||||
public static Extension create(char key, StringTokenIterator val, ParseStatus sts) {
|
||||
if (sts.isError()) {
|
||||
return null;
|
||||
}
|
||||
if (val.isDone()) {
|
||||
sts.errorIndex = val.currentStart();
|
||||
sts.errorMsg = "Missing extension subtag for extension :" + key;
|
||||
return null;
|
||||
}
|
||||
|
||||
Extension ext = null;
|
||||
key = AsciiUtil.toLower(key);
|
||||
|
||||
switch (key) {
|
||||
case UnicodeLocaleExtension.SINGLETON:
|
||||
ext = new UnicodeLocaleExtension();
|
||||
break;
|
||||
case PrivateuseExtension.SINGLETON:
|
||||
ext = new PrivateuseExtension();
|
||||
break;
|
||||
default:
|
||||
ext = new Extension(key);
|
||||
break;
|
||||
}
|
||||
|
||||
ext.setExtensionValue(val, sts);
|
||||
|
||||
if (ext.getValue() == null) {
|
||||
// return null only when nothing parsed.
|
||||
return null;
|
||||
}
|
||||
|
||||
return ext;
|
||||
}
|
||||
|
||||
|
||||
protected void setExtensionValue(StringTokenIterator itr, ParseStatus sts) {
|
||||
if (sts.isError() || itr.isDone()) {
|
||||
_value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
while (!itr.isDone()) {
|
||||
String s = itr.current();
|
||||
if (!LanguageTag.isExtensionSubtag(s)) {
|
||||
break;
|
||||
}
|
||||
s = LanguageTag.canonicalizeExtensionSubtag(s);
|
||||
if (buf.length() != 0) {
|
||||
buf.append(LanguageTag.SEP);
|
||||
}
|
||||
buf.append(s);
|
||||
sts.parseLength = itr.currentEnd();
|
||||
itr.next();
|
||||
}
|
||||
|
||||
if (buf.length() == 0) {
|
||||
sts.errorIndex = itr.currentStart();
|
||||
sts.errorMsg = "Invalid extension subtag: " + itr.current();
|
||||
_value = null;
|
||||
} else {
|
||||
_value = buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,46 +6,44 @@
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public final class InternalLocaleBuilder {
|
||||
import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
|
||||
|
||||
public static final char PRIVATEUSEKEY = 'x';
|
||||
public final class InternalLocaleBuilder {
|
||||
|
||||
private String _language = "";
|
||||
private String _script = "";
|
||||
private String _region = "";
|
||||
private String _variant = "";
|
||||
private SortedMap<Character, Extension> _extMap;
|
||||
|
||||
private FieldHandler _handler = FieldHandler.DEFAULT;
|
||||
private final boolean _lenientVariant;
|
||||
|
||||
private HashMap<Character, String> _extMap;
|
||||
private HashMap<String, String> _kwdMap;
|
||||
|
||||
private static final char LDMLSINGLETON = 'u';
|
||||
|
||||
private static final String LANGTAGSEP = "-";
|
||||
private static final String LOCALESEP = "_";
|
||||
|
||||
private static final int DEFAULTMAPCAPACITY = 4;
|
||||
|
||||
public InternalLocaleBuilder() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public InternalLocaleBuilder(FieldHandler handler) {
|
||||
_handler = handler;
|
||||
public InternalLocaleBuilder(boolean lenientVariant) {
|
||||
_lenientVariant = lenientVariant;
|
||||
}
|
||||
|
||||
public boolean isLenientVariant() {
|
||||
return _lenientVariant;
|
||||
}
|
||||
|
||||
public InternalLocaleBuilder setLanguage(String language) throws LocaleSyntaxException {
|
||||
String newval = "";
|
||||
if (language.length() > 0) {
|
||||
newval = _handler.process(FieldType.LANGUAGE, language);
|
||||
if (newval == null) {
|
||||
throw new LocaleSyntaxException("Ill-formed language: " + language);
|
||||
if (!LanguageTag.isLanguage(language)) {
|
||||
throw new LocaleSyntaxException("Ill-formed language: " + language, 0);
|
||||
}
|
||||
newval = LanguageTag.canonicalizeLanguage(language);
|
||||
}
|
||||
_language = newval;
|
||||
return this;
|
||||
@ -54,10 +52,10 @@ public final class InternalLocaleBuilder {
|
||||
public InternalLocaleBuilder setScript(String script) throws LocaleSyntaxException {
|
||||
String newval = "";
|
||||
if (script.length() > 0) {
|
||||
newval = _handler.process(FieldType.SCRIPT, script);
|
||||
if (newval == null) {
|
||||
throw new LocaleSyntaxException("Ill-formed script: " + script);
|
||||
if (!LanguageTag.isScript(script)) {
|
||||
throw new LocaleSyntaxException("Ill-formed script: " + script, 0);
|
||||
}
|
||||
newval = LanguageTag.canonicalizeScript(script);
|
||||
}
|
||||
_script = newval;
|
||||
return this;
|
||||
@ -66,10 +64,10 @@ public final class InternalLocaleBuilder {
|
||||
public InternalLocaleBuilder setRegion(String region) throws LocaleSyntaxException {
|
||||
String newval = "";
|
||||
if (region.length() > 0) {
|
||||
newval = _handler.process(FieldType.REGION, region);
|
||||
if (newval == null) {
|
||||
if (!LanguageTag.isRegion(region)) {
|
||||
throw new LocaleSyntaxException("Ill-formed region: " + region);
|
||||
}
|
||||
newval = LanguageTag.canonicalizeRegion(region);
|
||||
}
|
||||
_region = newval;
|
||||
return this;
|
||||
@ -78,96 +76,160 @@ public final class InternalLocaleBuilder {
|
||||
public InternalLocaleBuilder setVariant(String variant) throws LocaleSyntaxException {
|
||||
String newval = "";
|
||||
if (variant.length() > 0) {
|
||||
newval = _handler.process(FieldType.VARIANT, variant);
|
||||
if (newval == null) {
|
||||
throw new LocaleSyntaxException("Ill-formed variant: " + variant);
|
||||
if (_lenientVariant) {
|
||||
newval = variant;
|
||||
} else {
|
||||
newval = processVariant(variant);
|
||||
}
|
||||
}
|
||||
_variant = newval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InternalLocaleBuilder setLDMLExtensionValue(String key, String type) throws LocaleSyntaxException {
|
||||
public InternalLocaleBuilder setUnicodeLocaleExtension(String key, String type) throws LocaleSyntaxException {
|
||||
if (key.length() == 0) {
|
||||
throw new LocaleSyntaxException("Empty LDML extension key");
|
||||
throw new LocaleSyntaxException("Empty Unicode locale extension key");
|
||||
}
|
||||
String kwdkey = _handler.process(FieldType.LDMLKEY, key);
|
||||
if (kwdkey == null) {
|
||||
throw new LocaleSyntaxException("Ill-formed LDML extension key: " + key);
|
||||
if (!UnicodeLocaleExtension.isKey(key)) {
|
||||
throw new LocaleSyntaxException("Ill-formed Unicode locale extension key: " + key, 0);
|
||||
}
|
||||
|
||||
key = UnicodeLocaleExtension.canonicalizeKey(key);
|
||||
|
||||
UnicodeLocaleExtension ulext = null;
|
||||
if (_extMap != null) {
|
||||
ulext = (UnicodeLocaleExtension)_extMap.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
|
||||
}
|
||||
|
||||
if (type.length() == 0) {
|
||||
if (_kwdMap != null) {
|
||||
_kwdMap.remove(kwdkey);
|
||||
if (ulext != null) {
|
||||
ulext.remove(key);
|
||||
}
|
||||
} else {
|
||||
String kwdtype = _handler.process(FieldType.LDMLTYPE, type);
|
||||
if (kwdtype == null) {
|
||||
throw new LocaleSyntaxException("Ill-formed LDML extension value: " + type);
|
||||
StringBuilder buf = new StringBuilder();
|
||||
StringTokenIterator sti = new StringTokenIterator(type, LanguageTag.SEP);
|
||||
for (String subtag = sti.first(); !sti.isDone(); subtag = sti.next()) {
|
||||
if (!UnicodeLocaleExtension.isTypeSubtag(subtag)) {
|
||||
throw new LocaleSyntaxException("Ill-formed Unicode locale extension type: " + type, sti.currentStart());
|
||||
}
|
||||
if (buf.length() > 0) {
|
||||
buf.append(LanguageTag.SEP);
|
||||
}
|
||||
buf.append(UnicodeLocaleExtension.canonicalizeTypeSubtag(subtag));
|
||||
}
|
||||
if (_kwdMap == null) {
|
||||
_kwdMap = new HashMap<String, String>(DEFAULTMAPCAPACITY);
|
||||
if (ulext == null) {
|
||||
SortedMap<String, String> ktmap = new TreeMap<String, String>();
|
||||
ktmap.put(key, buf.toString());
|
||||
ulext = new UnicodeLocaleExtension(ktmap);
|
||||
if (_extMap == null) {
|
||||
_extMap = new TreeMap<Character, Extension>();
|
||||
}
|
||||
_extMap.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), ulext);
|
||||
} else {
|
||||
ulext.put(key, buf.toString());
|
||||
}
|
||||
_kwdMap.put(kwdkey, kwdtype);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public InternalLocaleBuilder setExtension(char singleton, String value) throws LocaleSyntaxException {
|
||||
if (!LocaleExtensions.isValidExtensionKey(singleton)) {
|
||||
String strSingleton = String.valueOf(singleton);
|
||||
if (!LanguageTag.isExtensionSingleton(strSingleton) && !LanguageTag.isPrivateuseSingleton(strSingleton)) {
|
||||
throw new LocaleSyntaxException("Ill-formed extension key: " + singleton);
|
||||
}
|
||||
|
||||
// singleton char to lower case
|
||||
singleton = AsciiUtil.toLower(singleton);
|
||||
strSingleton = LanguageTag.canonicalizeExtensionSingleton(strSingleton);
|
||||
Character key = Character.valueOf(strSingleton.charAt(0));
|
||||
|
||||
if (singleton == LDMLSINGLETON) {
|
||||
// 'u' extension reserved for locale keywords
|
||||
if (_kwdMap != null) {
|
||||
// blow out the keywords currently set
|
||||
_kwdMap.clear();
|
||||
}
|
||||
// parse locale keyword extension subtags
|
||||
String[] kwdtags = (value.replaceAll(LOCALESEP, LANGTAGSEP)).split(LANGTAGSEP);
|
||||
if ((kwdtags.length % 2) != 0) {
|
||||
// number of keyword subtags must be even number
|
||||
throw new LocaleSyntaxException("Ill-formed LDML extension key/value pairs: " + value);
|
||||
}
|
||||
int idx = 0;
|
||||
while (idx < kwdtags.length) {
|
||||
String kwdkey = _handler.process(FieldType.LDMLKEY, kwdtags[idx++]);
|
||||
String kwdtype = _handler.process(FieldType.LDMLTYPE, kwdtags[idx++]);
|
||||
if (kwdkey == null || kwdkey.length() == 0
|
||||
|| kwdtype == null || kwdtype.length() == 0) {
|
||||
throw new LocaleSyntaxException("Ill-formed LDML extension key/value pairs: " + value);
|
||||
}
|
||||
if (_kwdMap == null) {
|
||||
_kwdMap = new HashMap<String, String>(kwdtags.length / 2);
|
||||
}
|
||||
String prevVal = _kwdMap.put(kwdkey, kwdtype);
|
||||
if (prevVal != null) {
|
||||
throw new LocaleSyntaxException("Ill-formed LDML extension containing duplicated keys: " + value);
|
||||
}
|
||||
if (value.length() == 0) {
|
||||
if (_extMap != null) {
|
||||
_extMap.remove(key);
|
||||
}
|
||||
} else {
|
||||
// other extensions including privateuse
|
||||
if (value.length() == 0) {
|
||||
if (_extMap != null) {
|
||||
_extMap.remove(Character.valueOf(singleton));
|
||||
}
|
||||
} else {
|
||||
FieldType ftype = (singleton == PRIVATEUSEKEY) ? FieldType.PRIVATEUSE : FieldType.EXTENSION;
|
||||
String extval = _handler.process(ftype, value);
|
||||
if (extval == null) {
|
||||
throw new LocaleSyntaxException("Ill-formed LDML extension value: " + value);
|
||||
}
|
||||
if (_extMap == null) {
|
||||
_extMap = new HashMap<Character, String>(DEFAULTMAPCAPACITY);
|
||||
}
|
||||
_extMap.put(Character.valueOf(singleton), extval);
|
||||
StringTokenIterator sti = new StringTokenIterator(value, LanguageTag.SEP);
|
||||
ParseStatus sts = new ParseStatus();
|
||||
|
||||
Extension ext = Extension.create(key.charValue(), sti, sts);
|
||||
if (sts.isError()) {
|
||||
throw new LocaleSyntaxException(sts.errorMsg, sts.errorIndex);
|
||||
}
|
||||
if (sts.parseLength != value.length() || ext == null) {
|
||||
throw new LocaleSyntaxException("Ill-formed extension value: " + value, sti.currentStart());
|
||||
}
|
||||
if (_extMap == null) {
|
||||
_extMap = new TreeMap<Character, Extension>();
|
||||
}
|
||||
_extMap.put(key, ext);
|
||||
}
|
||||
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();
|
||||
|
||||
// Validate base locale fields before updating internal state.
|
||||
// LocaleExtensions always store validated/canonicalized values,
|
||||
// so no checks are necessary.
|
||||
if (language.length() > 0) {
|
||||
if (!LanguageTag.isLanguage(language)) {
|
||||
throw new LocaleSyntaxException("Ill-formed language: " + language);
|
||||
}
|
||||
language = LanguageTag.canonicalizeLanguage(language);
|
||||
}
|
||||
if (script.length() > 0) {
|
||||
if (!LanguageTag.isScript(script)) {
|
||||
throw new LocaleSyntaxException("Ill-formed script: " + script);
|
||||
}
|
||||
script = LanguageTag.canonicalizeScript(script);
|
||||
}
|
||||
if (region.length() > 0) {
|
||||
if (!LanguageTag.isRegion(region)) {
|
||||
throw new LocaleSyntaxException("Ill-formed region: " + region);
|
||||
}
|
||||
region = LanguageTag.canonicalizeRegion(region);
|
||||
}
|
||||
if (_lenientVariant) {
|
||||
// In lenient variant mode, parse special private use value
|
||||
// reserved for Java Locale.
|
||||
String privuse = extensions.getExtensionValue(Character.valueOf(LanguageTag.PRIVATEUSE.charAt(0)));
|
||||
if (privuse != null) {
|
||||
variant = LanguageTag.getJavaCompatibleVariant(variant, privuse);
|
||||
}
|
||||
} else {
|
||||
if (variant.length() > 0) {
|
||||
variant = processVariant(variant);
|
||||
}
|
||||
}
|
||||
|
||||
// update builder's internal fields
|
||||
_language = language;
|
||||
_script = script;
|
||||
_region = region;
|
||||
_variant = variant;
|
||||
|
||||
// empty extensions
|
||||
if (_extMap == null) {
|
||||
_extMap = new TreeMap<Character, Extension>();
|
||||
} else {
|
||||
_extMap.clear();
|
||||
}
|
||||
|
||||
Set<Character> extKeys = extensions.getKeys();
|
||||
for (Character key : extKeys) {
|
||||
Extension ext = extensions.getExtension(key);
|
||||
if (_lenientVariant && (ext instanceof PrivateuseExtension)) {
|
||||
String modPrivuse = LanguageTag.getJavaCompatiblePrivateuse(ext.getValue());
|
||||
if (!modPrivuse.equals(ext.getValue())) {
|
||||
ext = new PrivateuseExtension(modPrivuse);
|
||||
}
|
||||
}
|
||||
_extMap.put(key, ext);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -184,9 +246,6 @@ public final class InternalLocaleBuilder {
|
||||
if (_extMap != null) {
|
||||
_extMap.clear();
|
||||
}
|
||||
if (_kwdMap != null) {
|
||||
_kwdMap.clear();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -195,155 +254,28 @@ public final class InternalLocaleBuilder {
|
||||
}
|
||||
|
||||
public LocaleExtensions getLocaleExtensions() {
|
||||
TreeMap<Character, String> extMap = null;
|
||||
TreeMap<String, String> kwdMap = null;
|
||||
|
||||
// process keywords
|
||||
if (_kwdMap != null && _kwdMap.size() > 0) {
|
||||
Set<Map.Entry<String, String>> kwds = _kwdMap.entrySet();
|
||||
for (Map.Entry<String, String> kwd : kwds) {
|
||||
String key = kwd.getKey();
|
||||
String type = kwd.getValue();
|
||||
if (kwdMap == null) {
|
||||
kwdMap = new TreeMap<String, String>();
|
||||
}
|
||||
kwdMap.put(key.intern(), type.intern());
|
||||
}
|
||||
if (_extMap != null && _extMap.size() > 0) {
|
||||
return LocaleExtensions.getInstance(_extMap);
|
||||
}
|
||||
|
||||
// process extensions and privateuse
|
||||
if (_extMap != null) {
|
||||
Set<Map.Entry<Character, String>> exts = _extMap.entrySet();
|
||||
for (Map.Entry<Character, String> ext : exts) {
|
||||
Character key = ext.getKey();
|
||||
String value = ext.getValue();
|
||||
if (extMap == null) {
|
||||
extMap = new TreeMap<Character, String>();
|
||||
}
|
||||
extMap.put(key, value.intern());
|
||||
}
|
||||
}
|
||||
|
||||
// set canonical locale keyword extension string to the extension map
|
||||
if (kwdMap != null) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
LocaleExtensions.keywordsToString(kwdMap, buf);
|
||||
if (extMap == null) {
|
||||
extMap = new TreeMap<Character, String>();
|
||||
}
|
||||
extMap.put(Character.valueOf(LDMLSINGLETON), buf.toString().intern());
|
||||
}
|
||||
|
||||
return LocaleExtensions.getInstance(extMap, kwdMap);
|
||||
return LocaleExtensions.EMPTY_EXTENSIONS;
|
||||
}
|
||||
|
||||
protected enum FieldType {
|
||||
LANGUAGE,
|
||||
SCRIPT,
|
||||
REGION,
|
||||
VARIANT,
|
||||
LDMLKEY,
|
||||
LDMLTYPE,
|
||||
EXTENSION,
|
||||
PRIVATEUSE
|
||||
}
|
||||
private String processVariant(String variant) throws LocaleSyntaxException {
|
||||
StringTokenIterator sti = new StringTokenIterator(variant, LOCALESEP);
|
||||
ParseStatus sts = new ParseStatus();
|
||||
|
||||
public static class FieldHandler {
|
||||
public static FieldHandler DEFAULT = new FieldHandler();
|
||||
|
||||
protected FieldHandler() {
|
||||
List<String> variants = LanguageTag.DEFAULT_PARSER.parseVariants(sti, sts);
|
||||
if (sts.parseLength != variant.length()) {
|
||||
throw new LocaleSyntaxException("Ill-formed variant: " + variant, sti.currentStart());
|
||||
}
|
||||
|
||||
public String process(FieldType type, String value) {
|
||||
value = map(type, value);
|
||||
if (value.length() > 0 && !validate(type, value)) {
|
||||
return null;
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (String var : variants) {
|
||||
if (buf.length() != 0) {
|
||||
buf.append(LOCALESEP);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected String map(FieldType type, String value) {
|
||||
switch (type) {
|
||||
case LANGUAGE:
|
||||
value = AsciiUtil.toLowerString(value);
|
||||
break;
|
||||
case SCRIPT:
|
||||
if (value.length() > 0) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(AsciiUtil.toUpper(value.charAt(0)));
|
||||
for (int i = 1; i < value.length(); i++) {
|
||||
buf.append(AsciiUtil.toLower(value.charAt(i)));
|
||||
}
|
||||
value = buf.toString();
|
||||
}
|
||||
break;
|
||||
case REGION:
|
||||
value = AsciiUtil.toUpperString(value);
|
||||
break;
|
||||
case VARIANT:
|
||||
// Java variant is case sensitive - so no case mapping here
|
||||
value = value.replaceAll(LANGTAGSEP, LOCALESEP);
|
||||
break;
|
||||
case LDMLKEY:
|
||||
case LDMLTYPE:
|
||||
case EXTENSION:
|
||||
case PRIVATEUSE:
|
||||
value = AsciiUtil.toLowerString(value).replaceAll(LOCALESEP, LANGTAGSEP);
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected boolean validate(FieldType type, String value) {
|
||||
boolean isValid = false;
|
||||
String[] subtags;
|
||||
|
||||
switch (type) {
|
||||
case LANGUAGE:
|
||||
isValid = LanguageTag.isLanguageSubtag(value);
|
||||
break;
|
||||
case SCRIPT:
|
||||
isValid = LanguageTag.isScriptSubtag(value);
|
||||
break;
|
||||
case REGION:
|
||||
isValid = LanguageTag.isRegionSubtag(value);
|
||||
break;
|
||||
case VARIANT:
|
||||
// variant field could have multiple subtags
|
||||
subtags = value.split(LOCALESEP);
|
||||
for (String subtag : subtags) {
|
||||
isValid = LanguageTag.isVariantSubtag(subtag);
|
||||
if (!isValid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LDMLKEY:
|
||||
isValid = LocaleExtensions.isValidLDMLKey(value);
|
||||
break;
|
||||
case LDMLTYPE:
|
||||
isValid = LocaleExtensions.isValidLDMLType(value);
|
||||
break;
|
||||
case EXTENSION:
|
||||
subtags = value.split(LANGTAGSEP);
|
||||
for (String subtag : subtags) {
|
||||
isValid = LanguageTag.isExtensionSubtag(subtag);
|
||||
if (!isValid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PRIVATEUSE:
|
||||
subtags = value.split(LANGTAGSEP);
|
||||
for (String subtag : subtags) {
|
||||
isValid = LanguageTag.isPrivateuseValueSubtag(subtag);
|
||||
if (!isValid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return isValid;
|
||||
buf.append(var);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,260 +7,111 @@
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public final class LocaleExtensions {
|
||||
public static final LocaleExtensions EMPTY_EXTENSIONS = new LocaleExtensions("");
|
||||
|
||||
private String _extensions;
|
||||
private TreeMap<Character, String> _extMap;
|
||||
private TreeMap<String, String> _kwdMap;
|
||||
|
||||
private static final String LOCALEEXTSEP = "-";
|
||||
private static final String LDMLSINGLETON = "u";
|
||||
private static final String PRIVUSE = "x";
|
||||
private static final int MINLEN = 3; // minimum length of string representation "x-?"
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
private LocaleExtensions(String extensions) {
|
||||
_extensions = extensions == null ? "" : extensions;
|
||||
public class LocaleExtensions {
|
||||
private SortedMap<Character, Extension> _map = EMPTY_MAP;
|
||||
private String _id = "";
|
||||
|
||||
private static final SortedMap<Character, Extension> EMPTY_MAP =
|
||||
Collections.unmodifiableSortedMap(new TreeMap<Character, Extension>());
|
||||
|
||||
private static final LocaleObjectCache<String, LocaleExtensions> LOCALEEXTENSIONS_CACHE =
|
||||
new LocaleObjectCache<String, LocaleExtensions>();
|
||||
|
||||
|
||||
public static LocaleExtensions EMPTY_EXTENSIONS = new LocaleExtensions();
|
||||
|
||||
private LocaleExtensions() {
|
||||
}
|
||||
|
||||
public static LocaleExtensions getInstance(String extensions) {
|
||||
if (extensions == null || extensions.length() == 0) {
|
||||
static LocaleExtensions getInstance(SortedMap<Character, Extension> map) {
|
||||
if (map == null || map.isEmpty()) {
|
||||
return EMPTY_EXTENSIONS;
|
||||
}
|
||||
|
||||
extensions = AsciiUtil.toLowerString(extensions).replaceAll("_", LOCALEEXTSEP);
|
||||
|
||||
if (extensions.length() < MINLEN) {
|
||||
// malformed extensions - too short
|
||||
return new LocaleExtensions(extensions);
|
||||
String id = getID(map);
|
||||
LocaleExtensions exts = LOCALEEXTENSIONS_CACHE.get(id);
|
||||
if (exts == null) {
|
||||
exts = new LocaleExtensions();
|
||||
exts._map = new TreeMap<Character, Extension>(map);
|
||||
exts._id = id;
|
||||
}
|
||||
return exts;
|
||||
}
|
||||
|
||||
TreeMap<Character, String> extMap = null;
|
||||
TreeMap<String, String> kwdMap = null;
|
||||
boolean bParseFailure = false;
|
||||
|
||||
// parse the extension subtags
|
||||
String[] subtags = extensions.split(LOCALEEXTSEP);
|
||||
String letter = null;
|
||||
extMap = new TreeMap<Character, String>();
|
||||
private static String getID(SortedMap<Character, Extension> map) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
boolean inLocaleKeywords = false;
|
||||
boolean inPrivateUse = false;
|
||||
String kwkey = null;
|
||||
|
||||
for (int i = 0; i < subtags.length; i++) {
|
||||
if (subtags[i].length() == 0) {
|
||||
// empty subtag
|
||||
bParseFailure = true;
|
||||
break;
|
||||
}
|
||||
if (subtags[i].length() == 1 && !inPrivateUse) {
|
||||
if (letter != null) {
|
||||
// next extension singleton
|
||||
if (extMap.containsKey(subtags[i])) {
|
||||
// duplicated singleton extension letter
|
||||
bParseFailure = true;
|
||||
break;
|
||||
}
|
||||
// write out the previous extension
|
||||
if (inLocaleKeywords) {
|
||||
if (kwkey != null) {
|
||||
// no locale keyword key
|
||||
bParseFailure = true;
|
||||
break;
|
||||
}
|
||||
// creating a single string including locale keyword key/type pairs
|
||||
keywordsToString(kwdMap, buf);
|
||||
inLocaleKeywords = false;
|
||||
}
|
||||
if (buf.length() == 0) {
|
||||
// empty subtag
|
||||
bParseFailure = true;
|
||||
break;
|
||||
}
|
||||
extMap.put(Character.valueOf(letter.charAt(0)), buf.toString().intern());
|
||||
Extension privuse = null;
|
||||
if (map != null && !map.isEmpty()) {
|
||||
Set<Entry<Character, Extension>> entries = map.entrySet();
|
||||
for (Entry<Character, Extension> entry : entries) {
|
||||
Character key = entry.getKey();
|
||||
if (key.charValue() == LanguageTag.PRIVATEUSE.charAt(0)) {
|
||||
privuse = entry.getValue();
|
||||
continue;
|
||||
}
|
||||
// preparation for next extension
|
||||
if (subtags[i].equals(LDMLSINGLETON)) {
|
||||
kwdMap = new TreeMap<String, String>();
|
||||
inLocaleKeywords = true;
|
||||
} else if (subtags[i].equals(PRIVUSE)) {
|
||||
inPrivateUse = true;
|
||||
}
|
||||
buf.setLength(0);
|
||||
letter = subtags[i];
|
||||
continue;
|
||||
}
|
||||
if (inLocaleKeywords) {
|
||||
if (kwkey == null) {
|
||||
kwkey = subtags[i];
|
||||
} else {
|
||||
kwdMap.put(kwkey.intern(), subtags[i].intern());
|
||||
kwkey = null;
|
||||
}
|
||||
} else {
|
||||
// append an extension/prvate use subtag
|
||||
if (buf.length() > 0) {
|
||||
buf.append(LOCALEEXTSEP);
|
||||
buf.append(LanguageTag.SEP);
|
||||
}
|
||||
buf.append(subtags[i]);
|
||||
buf.append(entry.getKey());
|
||||
buf.append(LanguageTag.SEP);
|
||||
buf.append(entry.getValue().getValue());
|
||||
}
|
||||
}
|
||||
if (!bParseFailure) {
|
||||
// process the last extension
|
||||
if (inLocaleKeywords) {
|
||||
if (kwkey != null) {
|
||||
bParseFailure = true;
|
||||
} else {
|
||||
// creating a single string including locale keyword key/type pairs
|
||||
keywordsToString(kwdMap, buf);
|
||||
}
|
||||
}
|
||||
if (buf.length() == 0) {
|
||||
// empty subtag at the end
|
||||
bParseFailure = true;
|
||||
} else {
|
||||
extMap.put(Character.valueOf(letter.charAt(0)), buf.toString().intern());
|
||||
if (privuse != null) {
|
||||
if (buf.length() > 0) {
|
||||
buf.append(LanguageTag.SEP);
|
||||
}
|
||||
buf.append(LanguageTag.PRIVATEUSE);
|
||||
buf.append(LanguageTag.SEP);
|
||||
buf.append(privuse.getValue());
|
||||
}
|
||||
|
||||
if (bParseFailure) {
|
||||
// parsing the extension string failed.
|
||||
// do not set any partial results in the result.
|
||||
return new LocaleExtensions(extensions);
|
||||
}
|
||||
|
||||
String canonical = extensionsToCanonicalString(extMap);
|
||||
LocaleExtensions le = new LocaleExtensions(canonical);
|
||||
le._extMap = extMap;
|
||||
le._kwdMap = kwdMap;
|
||||
|
||||
return le;
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
// This method assumes extension map and locale keyword map
|
||||
// are all in canonicalized format. This method is only used by
|
||||
// InternalLocaleBuilder.
|
||||
public static LocaleExtensions getInstance(TreeMap<Character, String> extMap, TreeMap<String ,String> kwdMap) {
|
||||
if (extMap == null) {
|
||||
return EMPTY_EXTENSIONS;
|
||||
public Set<Character> getKeys() {
|
||||
return Collections.unmodifiableSet(_map.keySet());
|
||||
}
|
||||
|
||||
public Extension getExtension(Character key) {
|
||||
return _map.get(key);
|
||||
}
|
||||
|
||||
public String getExtensionValue(Character key) {
|
||||
Extension ext = _map.get(key);
|
||||
if (ext == null) {
|
||||
return "";
|
||||
}
|
||||
String canonical = extensionsToCanonicalString(extMap);
|
||||
LocaleExtensions le = new LocaleExtensions(canonical);
|
||||
le._extMap = extMap;
|
||||
le._kwdMap = kwdMap;
|
||||
|
||||
return le;
|
||||
return ext.getValue();
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return (this == obj) ||
|
||||
((obj instanceof LocaleExtensions) && _extensions == (((LocaleExtensions)obj)._extensions));
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return _extensions.hashCode();
|
||||
}
|
||||
|
||||
public Set<Character> getExtensionKeys() {
|
||||
if (_extMap != null) {
|
||||
return Collections.unmodifiableSet(_extMap.keySet());
|
||||
public Set<String> getUnicodeLocaleKeys() {
|
||||
Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
|
||||
if (ext == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return null;
|
||||
assert (ext instanceof UnicodeLocaleExtension);
|
||||
return ((UnicodeLocaleExtension)ext).getKeys();
|
||||
}
|
||||
|
||||
public String getExtensionValue(char key) {
|
||||
if (_extMap != null) {
|
||||
return _extMap.get(Character.valueOf(key));
|
||||
public String getUnicodeLocaleType(String unicodeLocaleKey) {
|
||||
Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
|
||||
if (ext == null) {
|
||||
return "";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> getLDMLKeywordKeys() {
|
||||
if (_kwdMap != null) {
|
||||
return Collections.unmodifiableSet(_kwdMap.keySet());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getLDMLKeywordType(String key) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException("LDML key must not be null");
|
||||
}
|
||||
if (_kwdMap != null) {
|
||||
return _kwdMap.get(key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCanonicalString() {
|
||||
return _extensions;
|
||||
assert (ext instanceof UnicodeLocaleExtension);
|
||||
return ((UnicodeLocaleExtension)ext).getType(unicodeLocaleKey);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return _extensions;
|
||||
return _id;
|
||||
}
|
||||
|
||||
private static String extensionsToCanonicalString(TreeMap<Character, String> extMap) {
|
||||
if (extMap == null || extMap.size() == 0) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder canonicalbuf = new StringBuilder();
|
||||
String privUseStr = null;
|
||||
if (extMap != null) {
|
||||
Set<Map.Entry<Character, String>> entries = extMap.entrySet();
|
||||
for (Map.Entry<Character, String> entry : entries) {
|
||||
Character key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
if (key.charValue() == PRIVUSE.charAt(0)) {
|
||||
privUseStr = value;
|
||||
continue;
|
||||
}
|
||||
if (canonicalbuf.length() > 0) {
|
||||
canonicalbuf.append(LOCALEEXTSEP);
|
||||
}
|
||||
canonicalbuf.append(key);
|
||||
canonicalbuf.append(LOCALEEXTSEP);
|
||||
canonicalbuf.append(value);
|
||||
}
|
||||
}
|
||||
if (privUseStr != null) {
|
||||
if (canonicalbuf.length() > 0) {
|
||||
canonicalbuf.append(LOCALEEXTSEP);
|
||||
}
|
||||
canonicalbuf.append(PRIVUSE);
|
||||
canonicalbuf.append(LOCALEEXTSEP);
|
||||
canonicalbuf.append(privUseStr);
|
||||
}
|
||||
return canonicalbuf.toString().intern();
|
||||
}
|
||||
|
||||
public static void keywordsToString(TreeMap<String, String> map, StringBuilder buf) {
|
||||
Set<Map.Entry<String, String>> entries = map.entrySet();
|
||||
for (Map.Entry<String, String> entry : entries) {
|
||||
if (buf.length() > 0) {
|
||||
buf.append(LOCALEEXTSEP);
|
||||
}
|
||||
buf.append(entry.getKey());
|
||||
buf.append(LOCALEEXTSEP);
|
||||
buf.append(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isValidExtensionKey(char key) {
|
||||
return AsciiUtil.isAlphaNumeric(key);
|
||||
}
|
||||
|
||||
public static boolean isValidLDMLKey(String key) {
|
||||
return (key.length() == 2) && AsciiUtil.isAlphaNumericString(key);
|
||||
}
|
||||
|
||||
public static boolean isValidLDMLType(String type) {
|
||||
return (type.length() >= 3) && (type.length() <= 8) && AsciiUtil.isAlphaNumericString(type);
|
||||
public static boolean isValidKey(String key) {
|
||||
return LanguageTag.isExtensionSingleton(key) || LanguageTag.isPrivateuseSingleton(key);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
|
||||
|
||||
public class PrivateuseExtension extends Extension {
|
||||
public static final char SINGLETON = 'x';
|
||||
|
||||
protected PrivateuseExtension() {
|
||||
super(SINGLETON);
|
||||
}
|
||||
|
||||
/*
|
||||
* package local constructor only used by LanguageTag implementation
|
||||
*/
|
||||
PrivateuseExtension(String privuse) {
|
||||
super(SINGLETON);
|
||||
_value = privuse;
|
||||
}
|
||||
|
||||
protected void setExtensionValue(StringTokenIterator itr, ParseStatus sts) {
|
||||
if (sts.isError() || itr.isDone()) {
|
||||
_value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
while (!itr.isDone()) {
|
||||
String s = itr.current();
|
||||
if (!LanguageTag.isPrivateuseSubtag(s)) {
|
||||
break;
|
||||
}
|
||||
s = LanguageTag.canonicalizePrivateuseSubtag(s);
|
||||
if (buf.length() != 0) {
|
||||
buf.append(LanguageTag.SEP);
|
||||
}
|
||||
buf.append(s);
|
||||
sts.parseLength = itr.currentEnd();
|
||||
itr.next();
|
||||
}
|
||||
|
||||
if (buf.length() == 0) {
|
||||
_value = null;
|
||||
} else {
|
||||
_value = buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009, 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,193 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl.locale;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
|
||||
|
||||
public class UnicodeLocaleExtension extends Extension {
|
||||
public static final char SINGLETON = 'u';
|
||||
|
||||
private SortedMap<String, String> _keyTypeMap;
|
||||
|
||||
protected UnicodeLocaleExtension() {
|
||||
super(SINGLETON);
|
||||
}
|
||||
|
||||
/*
|
||||
* Package local constructor only used by InternalLocaleBuilder
|
||||
*/
|
||||
UnicodeLocaleExtension(SortedMap<String, String> keyTypeMap) {
|
||||
super(SINGLETON);
|
||||
_keyTypeMap = keyTypeMap;
|
||||
updateStringValue();
|
||||
}
|
||||
|
||||
protected void setExtensionValue(StringTokenIterator itr, ParseStatus sts) {
|
||||
if (sts.isError() || itr.isDone()) {
|
||||
_value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
SortedMap<String, String> keyTypeMap = new TreeMap<String, String>();
|
||||
String ukey = null;
|
||||
StringBuilder buf = new StringBuilder();
|
||||
int typeEnd = -1;
|
||||
|
||||
while (!itr.isDone()) {
|
||||
String s = itr.current();
|
||||
|
||||
if (isTypeSubtag(s)) {
|
||||
if (ukey == null) {
|
||||
// key is expected
|
||||
sts.errorIndex = itr.currentStart();
|
||||
sts.errorMsg = "Invalid Unicode locale extension key: " + s;
|
||||
break;
|
||||
}
|
||||
if (buf.length() > 0) {
|
||||
buf.append(LanguageTag.SEP);
|
||||
}
|
||||
buf.append(canonicalizeTypeSubtag(s));
|
||||
typeEnd = itr.currentEnd();
|
||||
|
||||
if (!itr.hasNext()) {
|
||||
// emit the last key/type
|
||||
keyTypeMap.put(ukey, buf.toString());
|
||||
sts.parseLength = typeEnd;
|
||||
itr.next();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// key or others
|
||||
if (ukey != null) {
|
||||
if (buf.length() > 0) {
|
||||
// emit previous key and value
|
||||
keyTypeMap.put(ukey, buf.toString());
|
||||
sts.parseLength = typeEnd;
|
||||
} else {
|
||||
// type is expected
|
||||
sts.errorIndex = itr.currentStart();
|
||||
sts.errorMsg = "Invalid Unicode locale extension type: " + s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isKey(s)) {
|
||||
if (itr.hasNext()) {
|
||||
ukey = canonicalizeKey(s);
|
||||
buf.setLength(0);
|
||||
typeEnd = -1;
|
||||
} else {
|
||||
// missing type
|
||||
sts.errorIndex = itr.currentStart();
|
||||
sts.errorMsg = "Missing subtag for Unicode locale extension: " + s;
|
||||
itr.next();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// others
|
||||
if (keyTypeMap.size() == 0) {
|
||||
// key is expected
|
||||
sts.errorIndex = itr.currentStart();
|
||||
sts.errorMsg = "Invalid Unicode locale extension key: " + s;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
itr.next();
|
||||
}
|
||||
|
||||
if (keyTypeMap.size() == 0) {
|
||||
_value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_keyTypeMap = keyTypeMap;
|
||||
updateStringValue();
|
||||
}
|
||||
|
||||
public Set<String> getKeys() {
|
||||
if (_keyTypeMap == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return Collections.unmodifiableSet(_keyTypeMap.keySet());
|
||||
}
|
||||
|
||||
public String getType(String key) {
|
||||
String type = null;
|
||||
if (_keyTypeMap != null) {
|
||||
type = _keyTypeMap.get(key);
|
||||
}
|
||||
|
||||
return (type == null ? "" : type);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static String canonicalizeKey(String s) {
|
||||
return LanguageTag.canonicalizeExtensionSubtag(s);
|
||||
}
|
||||
|
||||
public static String canonicalizeTypeSubtag(String s) {
|
||||
return LanguageTag.canonicalizeExtensionSubtag(s);
|
||||
}
|
||||
|
||||
// These methods are only used by InterlaLocaleBuilder
|
||||
UnicodeLocaleExtension remove(String key) {
|
||||
if (_keyTypeMap != null) {
|
||||
_keyTypeMap.remove(key);
|
||||
updateStringValue();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
UnicodeLocaleExtension put(String key, String type) {
|
||||
if (_keyTypeMap != null) {
|
||||
_keyTypeMap.put(key, type);
|
||||
updateStringValue();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void updateStringValue() {
|
||||
_value = null;
|
||||
|
||||
if (_keyTypeMap != null) {
|
||||
// re-construct string representation
|
||||
StringBuilder valBuf = new StringBuilder();
|
||||
Set<Entry<String, String>> entries = _keyTypeMap.entrySet();
|
||||
boolean isFirst = true;
|
||||
for (Entry<String, String> e : entries) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
valBuf.append(LanguageTag.SEP);
|
||||
}
|
||||
valBuf.append(e.getKey());
|
||||
valBuf.append(LanguageTag.SEP);
|
||||
valBuf.append(e.getValue());
|
||||
}
|
||||
|
||||
if (valBuf.length() > 0) {
|
||||
_value = valBuf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@ public class LocaleBuilderTest extends TestFmwk {
|
||||
}
|
||||
|
||||
public void TestLocaleBuilder() {
|
||||
// First String "st": strict (default) / "lv": lenient variant
|
||||
// "L": +1 = language
|
||||
// "S": +1 = script
|
||||
// "R": +1 = region
|
||||
@ -33,31 +34,42 @@ public class LocaleBuilderTest extends TestFmwk {
|
||||
// "X": indicates an exception must be thrown
|
||||
// "T": +1 = expected language tag
|
||||
String[][] TESTCASE = {
|
||||
{"L", "en", "R", "us", "T", "en-US", "en_US"},
|
||||
{"L", "en", "R", "FR", "L", "fr", "T", "fr-FR", "fr_FR"},
|
||||
{"L", "123", "X"},
|
||||
{"R", "us", "T", "und-US", "_US"},
|
||||
{"R", "usa", "X"},
|
||||
{"R", "123", "L", "en", "T", "en-123", "en_123"},
|
||||
{"S", "LATN", "L", "DE", "T", "de-Latn", "de_Latn"},
|
||||
{"S", "latin", "X"},
|
||||
{"L", "th", "R", "th", "K", "nu", "thai", "T", "th-TH-u-nu-thai", "th_TH@numbers=thai"},
|
||||
{"E", "z", "ExtZ", "L", "en", "T", "en-z-extz", "en@z=extz"},
|
||||
{"L", "fr", "R", "FR", "P", "Yoshito-ICU", "T", "fr-FR-x-yoshito-icu", "fr_FR@x=yoshito-icu"},
|
||||
{"L", "ja", "R", "jp", "K", "ca", "japanese", "T", "ja-JP-u-ca-japanese", "ja_JP@calendar=japanese"},
|
||||
{"K", "co", "PHONEBK", "K", "ca", "gregory", "L", "De", "T", "de-u-ca-gregory-co-phonebk", "de@calendar=gregorian;collation=phonebook"},
|
||||
{"E", "o", "OPQR", "E", "a", "aBcD", "T", "und-a-abcd-o-opqr", "@a=abcd;o=opqr"},
|
||||
{"E", "u", "nu-thai-ca-gregory", "L", "TH", "T", "th-u-ca-gregory-nu-thai", "th@calendar=gregorian;numbers=thai"},
|
||||
{"L", "en", "K", "tz", "usnyc", "R", "US", "T", "en-US-u-tz-usnyc", "en_US@timezone=america/new_york"},
|
||||
{"L", "de", "K", "co", "phonebk", "K", "ks", "level1", "K", "kk", "true", "T", "de-u-co-phonebk-kk-true-ks-level1", "de@collation=phonebook;colnormalization=yes;colstrength=primary"},
|
||||
// {"L", "en", "V", "foooo_barrr", "T", "en-foooo-barrr", "en__FOOOO_BARRR"},
|
||||
{"st", "L", "en", "R", "us", "T", "en-US", "en_US"},
|
||||
{"st", "L", "en", "R", "FR", "L", "fr", "T", "fr-FR", "fr_FR"},
|
||||
{"st", "L", "123", "X"},
|
||||
{"st", "R", "us", "T", "und-US", "_US"},
|
||||
{"st", "R", "usa", "X"},
|
||||
{"st", "R", "123", "L", "en", "T", "en-123", "en_123"},
|
||||
{"st", "S", "LATN", "L", "DE", "T", "de-Latn", "de_Latn"},
|
||||
{"st", "S", "latin", "X"},
|
||||
{"st", "L", "th", "R", "th", "K", "nu", "thai", "T", "th-TH-u-nu-thai", "th_TH@numbers=thai"},
|
||||
{"st", "E", "z", "ExtZ", "L", "en", "T", "en-z-extz", "en@z=extz"},
|
||||
{"st", "L", "fr", "R", "FR", "P", "Yoshito-ICU", "T", "fr-FR-x-yoshito-icu", "fr_FR@x=yoshito-icu"},
|
||||
{"st", "L", "ja", "R", "jp", "K", "ca", "japanese", "T", "ja-JP-u-ca-japanese", "ja_JP@calendar=japanese"},
|
||||
{"st", "K", "co", "PHONEBK", "K", "ca", "gregory", "L", "De", "T", "de-u-ca-gregory-co-phonebk", "de@calendar=gregorian;collation=phonebook"},
|
||||
{"st", "E", "o", "OPQR", "E", "a", "aBcD", "T", "und-a-abcd-o-opqr", "@a=abcd;o=opqr"},
|
||||
{"st", "E", "u", "nu-thai-ca-gregory", "L", "TH", "T", "th-u-ca-gregory-nu-thai", "th@calendar=gregorian;numbers=thai"},
|
||||
{"st", "L", "en", "K", "tz", "usnyc", "R", "US", "T", "en-US-u-tz-usnyc", "en_US@timezone=america/new_york"},
|
||||
{"st", "L", "de", "K", "co", "phonebk", "K", "ks", "level1", "K", "kk", "true", "T", "de-u-co-phonebk-kk-true-ks-level1", "de@collation=phonebook;colnormalization=yes;colstrength=primary"},
|
||||
{"lv", "L", "en", "R", "us", "V", "Windows_XP", "T", "en-US-windows-x-variant-xp", "en_US_WINDOWS_XP"},
|
||||
};
|
||||
|
||||
Builder bld = new Builder();
|
||||
Builder bld_st = new Builder();
|
||||
Builder bld_lv = new Builder(true);
|
||||
|
||||
for (int tidx = 0; tidx < TESTCASE.length; tidx++) {
|
||||
bld.clear();
|
||||
int i = 0;
|
||||
String[] expected = null;
|
||||
|
||||
Builder bld = bld_st;
|
||||
String bldType = TESTCASE[tidx][i++];
|
||||
|
||||
if (bldType.equals("lv")) {
|
||||
bld = bld_lv;
|
||||
}
|
||||
|
||||
bld.clear();
|
||||
|
||||
while (true) {
|
||||
String method = TESTCASE[tidx][i++];
|
||||
try {
|
||||
@ -72,7 +84,7 @@ public class LocaleBuilderTest extends TestFmwk {
|
||||
} else if (method.equals("K")) {
|
||||
String key = TESTCASE[tidx][i++];
|
||||
String type = TESTCASE[tidx][i++];
|
||||
bld.setLDMLExtensionValue(key, type);
|
||||
bld.setUnicodeLocaleKeyword(key, type);
|
||||
} else if (method.equals("E")) {
|
||||
String key = TESTCASE[tidx][i++];
|
||||
String value = TESTCASE[tidx][i++];
|
||||
@ -99,7 +111,7 @@ public class LocaleBuilderTest extends TestFmwk {
|
||||
}
|
||||
}
|
||||
if (expected != null) {
|
||||
ULocale loc = bld.create();
|
||||
ULocale loc = bld.build();
|
||||
if (!expected[1].equals(loc.toString())) {
|
||||
errln("FAIL: Wrong locale ID - " + loc +
|
||||
" for test case: " + Arrays.toString(TESTCASE[tidx]));
|
||||
@ -122,13 +134,13 @@ public class LocaleBuilderTest extends TestFmwk {
|
||||
Builder bld = new Builder();
|
||||
try {
|
||||
bld.setLocale(loc);
|
||||
ULocale loc1 = bld.create();
|
||||
ULocale loc1 = bld.build();
|
||||
if (!loc.equals(loc1)) {
|
||||
errln("FAIL: Locale loc1 " + loc1 + " was returned by the builder. Expected " + loc);
|
||||
}
|
||||
bld.setLanguage("").setLDMLExtensionValue("ca", "buddhist")
|
||||
.setLanguage("TH").setLDMLExtensionValue("ca", "gregory");
|
||||
ULocale loc2 = bld.create();
|
||||
bld.setLanguage("").setUnicodeLocaleKeyword("ca", "buddhist")
|
||||
.setLanguage("TH").setUnicodeLocaleKeyword("ca", "gregory");
|
||||
ULocale loc2 = bld.build();
|
||||
if (!loc.equals(loc2)) {
|
||||
errln("FAIL: Locale loc2 " + loc2 + " was returned by the builder. Expected " + loc);
|
||||
}
|
||||
|
@ -3752,17 +3752,18 @@ public class ULocaleTest extends TestFmwk {
|
||||
{"_Latn", "und-Latn"},
|
||||
{"_DE", "und-DE"},
|
||||
{"und_FR", "und-FR"},
|
||||
{"th_TH_TH", "th-TH"},
|
||||
{"th_TH_TH", "th-TH-x-variant-th"},
|
||||
{"bogus", "bogus"},
|
||||
{"foooobarrr", "und"},
|
||||
//{"az_AZ_CYRL", "az-cyrl-az"}, /* ICU4J does not have this specia locale mapping */
|
||||
{"aa_BB_CYRL", "aa-BB"},
|
||||
{"aa_BB_CYRL", "aa-BB-x-variant-cyrl"},
|
||||
{"en_US_1234", "en-US-1234"},
|
||||
{"en_US_VARIANTA_VARIANTB", "en-US-varianta-variantb"},
|
||||
{"en_US_VARIANTB_VARIANTA", "en-US-varianta-variantb"},
|
||||
{"ja__9876_5432", "ja-5432-9876"},
|
||||
{"zh_Hant__VAR", "zh-Hant"},
|
||||
{"es__BADVARIANT_GOODVAR", "es-goodvar"},
|
||||
{"en_US_VARIANTB_VARIANTA", "en-US-variantb-varianta"},
|
||||
{"ja__9876_5432", "ja-9876-5432"},
|
||||
{"zh_Hant__VAR", "zh-Hant-x-variant-var"},
|
||||
{"es__BADVARIANT_GOODVAR", "es-x-variant-badvariant-goodvar"},
|
||||
{"es__GOODVAR_BADVARIANT", "es-goodvar-x-variant-badvariant"},
|
||||
{"en@calendar=gregorian", "en-u-ca-gregory"},
|
||||
{"de@collation=phonebook;calendar=gregorian", "de-u-ca-gregory-co-phonebk"},
|
||||
{"th@numbers=thai;z=extz;x=priv-use;a=exta", "th-a-exta-u-nu-thai-z-extz-x-priv-use"},
|
||||
@ -3782,41 +3783,44 @@ public class ULocaleTest extends TestFmwk {
|
||||
}
|
||||
|
||||
public void TestForLanguageTag() {
|
||||
final Integer NOERROR = Integer.valueOf(-1);
|
||||
|
||||
final Object[][] langtag_to_locale = {
|
||||
{"zh-cmn-CH", "cmn_CH", new Integer(9)},
|
||||
{"en", "en", new Integer(2)},
|
||||
{"en-us", "en_US", new Integer(5)},
|
||||
{"und-us", "_US", new Integer(6)},
|
||||
{"und-latn", "_Latn", new Integer(8)},
|
||||
{"en-us-posix", "en_US_POSIX", new Integer(11)},
|
||||
{"de-de_euro", "de", new Integer(2)},
|
||||
{"kok-in", "kok_IN", new Integer(6)},
|
||||
{"123", "", new Integer(0)},
|
||||
{"en_us", "", new Integer(0)},
|
||||
{"en-latn-x", "en_Latn", new Integer(7)},
|
||||
{"art-lojban", "jbo", new Integer(10)},
|
||||
{"zh-hakka", "hak", new Integer(8)},
|
||||
{"zh-cmn-CH", "cmn_CH", new Integer(9)},
|
||||
{"xxx-yy", "xxx_YY", new Integer(6)},
|
||||
{"fr-234", "fr_234", new Integer(6)},
|
||||
{"i-default", "", new Integer(9)},
|
||||
{"i-test", "", new Integer(0)},
|
||||
{"ja-jp-jp", "ja_JP", new Integer(5)},
|
||||
{"bogus", "bogus", new Integer(5)},
|
||||
{"boguslang", "", new Integer(0)},
|
||||
{"EN-lATN-us", "en_Latn_US", new Integer(10)},
|
||||
{"und-variant-1234", "__1234_VARIANT", new Integer(16)},
|
||||
{"und-varzero-var1-vartwo", "__VARZERO", new Integer(11)},
|
||||
{"en-u-ca-gregory", "en@calendar=gregorian", new Integer(15)},
|
||||
{"en-U-cu-USD", "en@currency=usd", new Integer(11)},
|
||||
{"ar-x-1-2-3", "ar@x=1-2-3", new Integer(10)},
|
||||
{"fr-u-nu-latn-cu-eur", "fr@currency=eur;numbers=latn", new Integer(19)},
|
||||
{"de-k-kext-u-co-phonebk-nu-latn", "de@collation=phonebook;k=kext;numbers=latn", new Integer(30)},
|
||||
{"ja-u-cu-jpy-ca-jp", "ja@currency=jpy", new Integer(/*11*/ 0)}, //TODO: need error index support for invalid LDML extension
|
||||
{"en-us-u-tz-usnyc", "en_US@timezone=america/new_york", new Integer(16)},
|
||||
// {"und-a-abc-def", "und@a=abc-def", new Integer(13)}, // ICU4C does not allow an locale with keywords, but without base locale
|
||||
{"und-a-abc-def", "@a=abc-def", new Integer(13)},
|
||||
{"zh-u-ca-chinese-x-u-ca-chinese", "zh@calendar=chinese;x=u-ca-chinese", new Integer(30)},
|
||||
{"ja-u-cu-jpy-ca-jp", "ja@currency=jpy", Integer.valueOf(15)},
|
||||
{"en", "en", NOERROR},
|
||||
{"en-us", "en_US", NOERROR},
|
||||
{"und-us", "_US", NOERROR},
|
||||
{"und-latn", "_Latn", NOERROR},
|
||||
{"en-us-posix", "en_US_POSIX", NOERROR},
|
||||
{"de-de_euro", "de", Integer.valueOf(3)},
|
||||
{"kok-in", "kok_IN", NOERROR},
|
||||
{"123", "", Integer.valueOf(0)},
|
||||
{"en_us", "", Integer.valueOf(0)},
|
||||
{"en-latn-x", "en_Latn", Integer.valueOf(8)},
|
||||
{"art-lojban", "jbo", NOERROR},
|
||||
{"zh-hakka", "hak", NOERROR},
|
||||
{"zh-cmn-CH", "cmn_CH", NOERROR},
|
||||
{"xxx-yy", "xxx_YY", NOERROR},
|
||||
{"fr-234", "fr_234", NOERROR},
|
||||
{"i-default", "", NOERROR},
|
||||
{"i-test", "", Integer.valueOf(0)},
|
||||
{"ja-jp-jp", "ja_JP", Integer.valueOf(6)},
|
||||
{"bogus", "bogus", NOERROR},
|
||||
{"boguslang", "", Integer.valueOf(0)},
|
||||
{"EN-lATN-us", "en_Latn_US", NOERROR},
|
||||
{"und-variant-1234", "__VARIANT_1234", NOERROR},
|
||||
{"und-varzero-var1-vartwo", "__VARZERO", Integer.valueOf(12)},
|
||||
{"en-u-ca-gregory", "en@calendar=gregorian", NOERROR},
|
||||
{"en-U-cu-USD", "en@currency=usd", NOERROR},
|
||||
{"ar-x-1-2-3", "ar@x=1-2-3", NOERROR},
|
||||
{"fr-u-nu-latn-cu-eur", "fr@currency=eur;numbers=latn", NOERROR},
|
||||
{"de-k-kext-u-co-phonebk-nu-latn", "de@collation=phonebook;k=kext;numbers=latn", NOERROR},
|
||||
{"ja-u-cu-jpy-ca-jp", "ja@currency=jpy", Integer.valueOf(15)},
|
||||
{"en-us-u-tz-usnyc", "en_US@timezone=america/new_york", NOERROR},
|
||||
{"und-a-abc-def", "@a=abc-def", NOERROR},
|
||||
{"zh-u-ca-chinese-x-u-ca-chinese", "zh@calendar=chinese;x=u-ca-chinese", NOERROR},
|
||||
{"fr--FR", "fr", Integer.valueOf(3)},
|
||||
{"fr-", "fr", Integer.valueOf(3)},
|
||||
};
|
||||
|
||||
for (int i = 0; i < langtag_to_locale.length; i++) {
|
||||
@ -3830,36 +3834,33 @@ public class ULocaleTest extends TestFmwk {
|
||||
}
|
||||
}
|
||||
|
||||
// Use locale builder to check the parsed length
|
||||
// Use locale builder to check errors
|
||||
for (int i = 0; i < langtag_to_locale.length; i++) {
|
||||
String tag = (String)langtag_to_locale[i][0];
|
||||
ULocale expected = new ULocale((String)langtag_to_locale[i][1]);
|
||||
int inLen = tag.length();
|
||||
int validLen = ((Integer)langtag_to_locale[i][2]).intValue();
|
||||
int errorIdx = ((Integer)langtag_to_locale[i][2]).intValue();
|
||||
|
||||
try {
|
||||
Builder bld = new Builder();
|
||||
bld.setLanguageTag(tag);
|
||||
ULocale loc = bld.create();
|
||||
ULocale loc = bld.build();
|
||||
|
||||
if (!loc.equals(expected)) {
|
||||
errln("FAIL: forLanguageTag returned locale [" + loc + "] for language tag [" + tag
|
||||
+ "] - expected: [" + expected + "]");
|
||||
}
|
||||
if (inLen != validLen) {
|
||||
if (errorIdx != NOERROR.intValue()) {
|
||||
errln("FAIL: Builder.setLanguageTag should throw an exception for input tag [" + tag + "]");
|
||||
}
|
||||
} catch (IllformedLocaleException ifle) {
|
||||
int errorIdx = ifle.getErrorIndex();
|
||||
int expectedErrIdx = (validLen == 0) ? 0 : validLen + 1;
|
||||
if (errorIdx != expectedErrIdx) {
|
||||
errln("FAIL: Builder.setLanguageTag returned error index " + errorIdx
|
||||
+ " for input language tag [" + tag + "] expected: " + expectedErrIdx);
|
||||
if (ifle.getErrorIndex() != errorIdx) {
|
||||
errln("FAIL: Builder.setLanguageTag returned error index " + ifle.getErrorIndex()
|
||||
+ " for input language tag [" + tag + "] expected: " + errorIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test that if you use any locale without keyword that you will get a NULL
|
||||
* string returned and not throw and exception.
|
||||
|
Loading…
Reference in New Issue
Block a user