blob: f1d09ce44e67143dd14cd4c57c988387cfba2ae7 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.impl.locale;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
public final class InternalLocaleBuilder {
private String _language = "";
private String _script = "";
private String _region = "";
private String _variant = "";
private SortedMap<Character, Extension> _extMap;
private final boolean _lenientVariant;
private static final String LOCALESEP = "_";
public InternalLocaleBuilder() {
this(false);
}
public InternalLocaleBuilder(boolean lenientVariant) {
_lenientVariant = lenientVariant;
}
public boolean isLenientVariant() {
return _lenientVariant;
}
public InternalLocaleBuilder setLanguage(String language) throws LocaleSyntaxException {
String newval = "";
if (language.length() > 0) {
if (!LanguageTag.isLanguage(language)) {
throw new LocaleSyntaxException("Ill-formed language: " + language, 0);
}
newval = LanguageTag.canonicalizeLanguage(language);
}
_language = newval;
return this;
}
public InternalLocaleBuilder setScript(String script) throws LocaleSyntaxException {
String newval = "";
if (script.length() > 0) {
if (!LanguageTag.isScript(script)) {
throw new LocaleSyntaxException("Ill-formed script: " + script, 0);
}
newval = LanguageTag.canonicalizeScript(script);
}
_script = newval;
return this;
}
public InternalLocaleBuilder setRegion(String region) throws LocaleSyntaxException {
String newval = "";
if (region.length() > 0) {
if (!LanguageTag.isRegion(region)) {
throw new LocaleSyntaxException("Ill-formed region: " + region);
}
newval = LanguageTag.canonicalizeRegion(region);
}
_region = newval;
return this;
}
public InternalLocaleBuilder setVariant(String variant) throws LocaleSyntaxException {
String newval = "";
if (variant.length() > 0) {
if (_lenientVariant) {
newval = variant;
} else {
newval = processVariant(variant);
}
}
_variant = newval;
return this;
}
public InternalLocaleBuilder setUnicodeLocaleExtension(String key, String type) throws LocaleSyntaxException {
if (key.length() == 0) {
throw new LocaleSyntaxException("Empty Unicode locale extension 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 (ulext != null) {
ulext.remove(key);
if (ulext.isEmpty()) {
_extMap.remove(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
}
}
} else {
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 (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());
}
}
return this;
}
public InternalLocaleBuilder setExtension(char singleton, String value) throws LocaleSyntaxException {
String strSingleton = String.valueOf(singleton);
if (!LanguageTag.isExtensionSingleton(strSingleton) && !LanguageTag.isPrivateuseSingleton(strSingleton)) {
throw new LocaleSyntaxException("Ill-formed extension key: " + singleton);
}
strSingleton = LanguageTag.canonicalizeExtensionSingleton(strSingleton);
Character key = Character.valueOf(strSingleton.charAt(0));
if (value.length() == 0) {
if (_extMap != null) {
_extMap.remove(key);
}
} else {
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;
}
public InternalLocaleBuilder clear() {
_language = "";
_script = "";
_region = "";
_variant = "";
removeLocaleExtensions();
return this;
}
public InternalLocaleBuilder removeLocaleExtensions() {
if (_extMap != null) {
_extMap.clear();
}
return this;
}
public BaseLocale getBaseLocale() {
return BaseLocale.getInstance(_language, _script, _region, _variant);
}
public LocaleExtensions getLocaleExtensions() {
if (_extMap != null && _extMap.size() > 0) {
return LocaleExtensions.getInstance(_extMap);
}
return LocaleExtensions.EMPTY_EXTENSIONS;
}
private String processVariant(String variant) throws LocaleSyntaxException {
StringTokenIterator sti = new StringTokenIterator(variant, LOCALESEP);
ParseStatus sts = new ParseStatus();
List<String> variants = LanguageTag.DEFAULT_PARSER.parseVariants(sti, sts);
if (sts.parseLength != variant.length()) {
throw new LocaleSyntaxException("Ill-formed variant: " + variant, sti.currentStart());
}
StringBuilder buf = new StringBuilder();
for (String var : variants) {
if (buf.length() != 0) {
buf.append(LOCALESEP);
}
buf.append(var);
}
return buf.toString();
}
}