/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.data; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * Language used in representations and preferences. A language tag is composed * of one or more parts: A primary language tag and a possibly empty series of * sub-tags. When formatted as a string, parts are separated by hyphens. * * @author Jerome Louvel */ public final class Language extends Metadata { /** All languages acceptable. */ public static final Language ALL = new Language("*", "All languages"); // [ifndef gwt] member /** * The default language of the JVM. * * @see java.util.Locale#getDefault() */ public static final Language DEFAULT = new Language(java.util.Locale .getDefault().getLanguage()); // [ifdef gwt] member uncomment // public static final Language DEFAULT = new Language("en", // "English language"); /** English language. */ public static final Language ENGLISH = new Language("en", "English language"); /** English language spoken in USA. */ public static final Language ENGLISH_US = new Language("en-us", "English language in USA"); /** French language. */ public static final Language FRENCH = new Language("fr", "French language"); /** French language spoken in France. */ public static final Language FRENCH_FRANCE = new Language("fr-fr", "French language in France"); /** Spanish language. */ public static final Language SPANISH = new Language("es", "Spanish language"); /** * Returns the language associated to a name. If an existing constant exists * then it is returned, otherwise a new instance is created. * * @param name * The name. * @return The associated language. */ public static Language valueOf(final String name) { Language result = null; if ((name != null) && !name.equals("")) { if (name.equalsIgnoreCase(ALL.getName())) { result = ALL; } else if (name.equalsIgnoreCase(ENGLISH.getName())) { result = ENGLISH; } else if (name.equalsIgnoreCase(ENGLISH_US.getName())) { result = ENGLISH_US; } else if (name.equalsIgnoreCase(FRENCH.getName())) { result = FRENCH; } else if (name.equalsIgnoreCase(FRENCH_FRANCE.getName())) { result = FRENCH_FRANCE; } else if (name.equalsIgnoreCase(SPANISH.getName())) { result = SPANISH; } else { result = new Language(name); } } return result; } /** The metadata main list of subtags taken from the metadata name. */ private volatile List<String> subTags; /** * Constructor. * * @param name * The name. */ public Language(final String name) { this(name, "Language or range of languages"); } /** * Constructor. * * @param name * The name. * @param description * The description. */ public Language(final String name, final String description) { super(name, description); this.subTags = null; } /** {@inheritDoc} */ @Override public boolean equals(final Object object) { return (object instanceof Language) && getName().equalsIgnoreCase(((Language) object).getName()); } @Override public Language getParent() { Language result = null; if ((getSubTags() != null) && !getSubTags().isEmpty()) { result = Language.valueOf(getPrimaryTag()); } else { result = equals(ALL) ? null : ALL; } return result; } /** * Returns the primary tag. * * @return The primary tag. */ public String getPrimaryTag() { final int separator = getName().indexOf('-'); if (separator == -1) { return getName(); } return getName().substring(0, separator); } /** * Returns the unmodifiable list of subtags. This list can be empty. * * @return The list of subtags for this language Tag. */ public List<String> getSubTags() { // Lazy initialization with double-check. List<String> v = this.subTags; if (v == null) { synchronized (this) { v = this.subTags; if (v == null) { List<String> tokens = new CopyOnWriteArrayList<String>(); if (getName() != null) { final String[] tags = getName().split("-"); if (tags.length > 0) { for (int i = 1; i < tags.length; i++) { tokens.add(tags[i]); } } } this.subTags = v = Collections.unmodifiableList(tokens); } } } return v; } /** {@inheritDoc} */ @Override public int hashCode() { return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); } /** * Indicates if a given language is included in the current one. The test is * true if both languages are equal or if the given language is within the * range of the current one. For example, ALL includes all languages. A null * language is considered as included into the current one. * <p> * Examples: * <ul> * <li>ENGLISH.includes(ENGLISH_US) -> true</li> * <li>ENGLISH_US.includes(ENGLISH) -> false</li> * </ul> * * @param included * The language to test for inclusion. * @return True if the language type is included in the current one. * @see #isCompatible(Metadata) */ public boolean includes(Metadata included) { boolean result = equals(ALL) || (included == null) || equals(included); if (!result && (included instanceof Language)) { Language includedLanguage = (Language) included; if (getPrimaryTag().equals(includedLanguage.getPrimaryTag())) { // Both languages are different if (getSubTags().equals(includedLanguage.getSubTags())) { result = true; } else if (getSubTags().isEmpty()) { result = true; } } } return result; } }