blob: e37cd8b98b2012afd72b769ad60f237c545c8376 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/
package org.apache.abdera2.common.lang;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.abdera2.common.lang.Subtag.Type;
/**
* Implementation of RFC 4646 Language Tags. Instances are immutable and
* safe for use by multiple threads. Iterators returned by calling
* the iterator() method, however, are not threadsafe and should only
* ever be accessed by a single thread.
*/
public final class Lang
extends SubtagSet {
private static final long serialVersionUID = -7095560018906537331L;
private final Locale locale;
private Subtag t_extlang, t_script, t_variant,
t_region, t_extension, t_privateuse;
public Lang() {
this(init(Locale.getDefault()));
}
public Lang(Locale locale) {
this(init(locale));
}
private static Subtag init(Locale locale) {
try {
return parse(locale.toString()).root;
} catch (Exception e) {
Subtag c = null, primary = new Subtag(Type.LANGUAGE, locale.getLanguage());
String country = locale.getCountry();
String variant = locale.getVariant();
if (country != null)
c = new Subtag(Type.REGION, country, primary);
if (variant != null)
new Subtag(Type.VARIANT, variant, c);
return primary;
}
}
public Lang(String lang) {
this(parse(lang).root);
}
Lang(Subtag root) {
super(root);
scan();
this.locale = initLocale();
}
private Locale initLocale() {
Subtag primary = language();
Subtag region = region();
Subtag variant = variant();
if (variant != null && region != null)
return new Locale(primary.toString(), region.toString(), variant.toString());
else if (region != null)
return new Locale(primary.toString(), region.toString());
else
return new Locale(primary.toString());
}
public Subtag language() {
return root;
}
public Locale locale() {
return locale;
}
private void scan() {
for (Subtag subtag : this) {
switch (subtag.type()) {
case EXTLANG:
t_extlang = subtag;
break;
case SCRIPT:
t_script = subtag;
break;
case VARIANT:
t_variant = subtag;
break;
case REGION:
t_region = subtag;
break;
case SINGLETON:
if (subtag.isExtensionSingleton() && t_extension == null)
t_extension = subtag;
else if (subtag.isPrivateSingleton())
t_privateuse = subtag;
default: break;
}
}
}
public Subtag extlang() {
return t_extlang;
}
public Subtag script() {
return t_script;
}
public Subtag region() {
return t_region;
}
public Subtag variant() {
return t_variant;
}
public Subtag extension() {
return t_extension;
}
public Subtag[] extensions() {
List<Subtag> list = new ArrayList<Subtag>();
Subtag extension = extension();
while(extension != null) {
list.add(extension);
extension = nextExtension(extension);
}
return list.toArray(new Subtag[list.size()]);
}
public Subtag extension(String name) {
Subtag subtag = extension(); // go to first extension;
while(subtag != null && !subtag.name().equals(name))
subtag = nextExtension(subtag);
return subtag;
}
private static Subtag nextExtension(Subtag subtag) {
subtag = subtag.next();
while (!subtag.isSingleton())
subtag = subtag.next();
return subtag.isExtensionSingleton() ? subtag : null;
}
public Subtag privateUse() {
return t_privateuse;
}
public Range asRange() {
return new Range(toString());
}
public Lang parent() {
Subtag root, prev, temp;
Iterator<Subtag> i = iterator();
temp = i.next();
root = new Subtag(
temp.type(),
temp.name());
prev = root;
while (i.hasNext()) {
temp = i.next();
if (i.hasNext())
prev = new Subtag(
temp.type(),
temp.name(),
prev);
}
return new Lang(root);
}
public Lang copy() {
Subtag root, prev, temp;
Iterator<Subtag> i = iterator();
temp = i.next();
root = new Subtag(
temp.type(),
temp.name());
prev = root;
while (i.hasNext()) {
temp = i.next();
prev = new Subtag(
temp.type(),
temp.name(),
prev);
}
return new Lang(root);
}
public boolean isGrandfathered() {
for (Subtag tag : this)
if (tag.type() == Type.GRANDFATHERED)
return true;
return false;
}
public boolean isChildOf(Lang lang) {
Range range = new Range(lang).appendWildcard();
return range.matches(this);
}
public boolean isParentOf(Lang lang) {
return lang.isChildOf(this);
}
// Parsing Logic
private static final String SEP = "\\s*[-_]\\s*";
private static final String language = "((?:[a-zA-Z]{2,3}(?:[-_][a-zA-Z]{3}){0,3})|[a-zA-Z]{4}|[a-zA-Z]{5,8})";
private static final String script = "((?:[-_][a-zA-Z]{4})?)";
private static final String region = "((?:[-_](?:(?:[a-zA-Z]{2})|(?:[0-9]{3})))?)";
private static final String variant = "((?:[-_](?:(?:[a-zA-Z0-9]{5,8})|(?:[0-9][a-zA-Z0-9]{3})))*)";
private static final String extension = "((?:[-_][a-wy-zA-WY-Z0-9](?:[-_][a-zA-Z0-9]{2,8})+)*)";
private static final String privateuse = "[xX](?:[-_][a-zA-Z0-9]{2,8})+";
private static final String _privateuse = "((?:[-_]" + privateuse + ")?)";
private static final String grandfathered =
"^(?:art[-_]lojban|cel[-_]gaulish|en[-_]GB[-_]oed|i[-_]ami|i[-_]bnn|i[-_]default|i[-_]enochian|i[-_]hak|i[-_]klingon|i[-_]lux|i[-_]mingo|i[-_]navajo|i[-_]pwn|i[-_]tao||i[-_]tay|i[-_]tsu|no[-_]bok|no[-_]nyn|sgn[-_]BE[-_]fr|sgn[-_]BE[-_]nl|sgn[-_]CH[-_]de|zh[-_]cmn|zh[-_]cmn[-_]Hans|zh[-_]cmn[-_]Hant|zh[-_]gan|zh[-_]guoyu|zh[-_]hakka|zh[-_]min|zh[-_]min[-_]nan|zh[-_]wuu|zh[-_]xiang|zh[-_]yue)$";
private static final String langtag = "^" + language + script + region + variant + extension + _privateuse + "$";
private static final Pattern p_langtag = Pattern.compile(langtag);
private static final Pattern p_privateuse = Pattern.compile("^" + privateuse + "$");
private static final Pattern p_grandfathered = Pattern.compile(grandfathered);
/**
* Parse a Lang tag
*/
public static Lang parse(String lang) {
if (lang == null || lang.length() == 0)
throw new IllegalArgumentException();
Subtag primary = null;
Matcher m = p_grandfathered.matcher(lang);
if (m.find()) {
String[] tags = lang.split(SEP);
Subtag current = null;
for (String tag : tags)
current = current == null ?
primary = new Subtag(Type.GRANDFATHERED, tag) :
new Subtag(Type.GRANDFATHERED, tag, current);
return new Lang(primary);
}
m = p_privateuse.matcher(lang);
if (m.find()) {
String[] tags = lang.split(SEP);
Subtag current = null;
for (String tag : tags)
current = current == null ?
primary = new Subtag(Type.SINGLETON, tag) :
new Subtag(Type.PRIVATEUSE, tag, current);
return new Lang(primary);
}
m = p_langtag.matcher(lang);
if (m.find()) {
String langtag = m.group(1);
String script = m.group(2);
String region = m.group(3);
String variant = m.group(4);
String extension = m.group(5);
String privateuse = m.group(6);
Subtag current = null;
String[] tags = langtag.split(SEP);
for (String tag : tags)
current = current == null ?
primary = new Subtag(Type.LANGUAGE, tag) :
new Subtag(Type.EXTLANG, tag, current);
if (script != null && script.length() > 0)
current = new Subtag(Type.SCRIPT, script.substring(1), current);
if (region != null && region.length() > 0)
current = new Subtag(Type.REGION, region.substring(1), current);
if (variant != null && variant.length() > 0) {
variant = variant.substring(1);
tags = variant.split(SEP);
for (String tag : tags)
current = new Subtag(Type.VARIANT, tag, current);
}
if (extension != null && extension.length() > 0) {
extension = extension.substring(1);
tags = extension.split(SEP);
current = new Subtag(Type.SINGLETON, tags[0], current);
for (int i = 1; i < tags.length; i++) {
String tag = tags[i];
current = new Subtag(
tag.length() == 1 ?
Type.SINGLETON :
Type.EXTENSION,
tag,
current);
}
}
if (privateuse != null && privateuse.length() > 0) {
privateuse = privateuse.substring(1);
tags = privateuse.split(SEP);
current = new Subtag(Type.SINGLETON, tags[0], current);
for (int i = 1; i < tags.length; i++)
current = new Subtag(Type.PRIVATEUSE, tags[i], current);
}
return new Lang(primary);
}
throw new IllegalArgumentException();
}
public static String fromLocale(Locale locale) {
return new Lang(locale).toString();
}
}