/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Jakob Hendeß
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.xlrnet.metadict.api.language;
import com.google.common.base.MoreObjects;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* The enum {@link Language} is used to describe the language of a word. Each language must have at least an identifier
* and a human-readable displayName. Although it is not necessary, it is recommended to use an ISO 639-1-compliant
* string as identifier. <br/> A language can also contain an optional dialect String.
*/
public class Language implements Serializable {
private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("[A-Za-z]+");
private static final ConcurrentMap<String, Language> languageCache = new ConcurrentHashMap<>();
/**
* Preconfigured language for English.
*/
public static final Language ENGLISH = forSimpleLanguage("en", "English");
/**
* Preconfigured language for German.
*/
public static final Language GERMAN = forSimpleLanguage("de", "German");
/**
* Preconfigured language for french.
*/
public static final Language FRENCH = forSimpleLanguage("fr", "French");
/**
* Preconfigured language for Spanish.
*/
public static final Language SPANISH = forSimpleLanguage("es", "Spanish");
/**
* Preconfigured language for Italian.
*/
public static final Language ITALIAN = forSimpleLanguage("it", "Italian");
/**
* Preconfigured language for Chinese.
*/
public static final Language CHINESE = forSimpleLanguage("cn", "Chinese");
/**
* Preconfigured language for Russian.
*/
public static final Language RUSSIAN = forSimpleLanguage("ru", "Russian");
/**
* Preconfigured language for Norwegian (either bokmål or nynorsk).
*/
public static final Language NORWEGIAN = forSimpleLanguage("no", "Norwegian");
/**
* Preconfigured language for Swedish.
*/
public static final Language SWEDISH = forSimpleLanguage("sv", "Swedish");
/**
* Preconfigured language for Finnish.
*/
public static final Language FINNISH = forSimpleLanguage("fi", "Finnish");
/**
* Preconfigured language for Turkish.
*/
public static final Language TURKISH = forSimpleLanguage("tr", "Turkish");
/**
* Preconfigured language for Dutch.
*/
public static final Language DUTCH = forSimpleLanguage("nl", "Dutch");
/**
* Preconfigured language for Portuguese.
*/
public static final Language PORTUGUESE = forSimpleLanguage("pt", "Portuguese");
/**
* Preconfigured language for Polish.
*/
public static final Language POLISH = forSimpleLanguage("pl", "Polish");
private static final ConcurrentMap<String, Language> dialectCache = new ConcurrentHashMap<>();
/**
* Preconfigured language for Norwegian bokmål.
*/
public static final Language NORWEGIAN_BOKMÅL = forSimpleLanguage("no", "Norwegian", "bo", "Bokmål");
/**
* Preconfigured language for Norwegian nynorsk.
*/
public static final Language NORWEGIAN_NYNORSK = forSimpleLanguage("no", "Norwegian", "ny", "Nynorsk");
private static final long serialVersionUID = 8370338805084250120L;
private final String identifier;
private final String displayName;
private final String dialect;
private final String dialectDisplayName;
protected Language(String identifier, String displayName, String dialect, String dialectDisplayName) {
this.identifier = identifier;
this.displayName = displayName;
this.dialect = dialect;
this.dialectDisplayName = dialectDisplayName;
}
/**
* Either creates a new language with the given identifier or returns the already registered language object for the
* given identifier. The identifier will always be converted to lower-case and is therefore case-insensitive. Valid
* identifiers consist of only characters (a to z) and must be at least one character long.
*
* @param identifier
* The identifier for the language. It is recommended to use an ISO 639-1-compliant string. See format
* restrictions above.
* @param displayName
* The name of the language that will be displayed to the client. If the language is already cached, the
* registered displayname will be returned.
* @return A {@link Language} object with the given identifier.
*/
@NotNull
public static Language forSimpleLanguage(String identifier, String displayName) {
checkNotNull(identifier, "Language identifier may not be null");
checkNotNull(displayName, "Display name may not be null");
checkArgument(isValidIdentifier(identifier), "Invalid language identifier: %s", identifier);
String key = identifier.toLowerCase();
languageCache.putIfAbsent(key, new Language(key, displayName, null, null));
return languageCache.get(key);
}
/**
* Return the {@link Language} object that is currently associated with the given identifier. This call will not
* create a new object but only return existing ones - not including dialects.
*
* @param identifier
* the language object that is currently associated with the given identifier.
* @return Either the resulting language or null if nothing found.
*/
@Nullable
public static Language getExistingLanguageById(String identifier) {
checkNotNull(identifier, "Language identifier may not be null");
return languageCache.get(identifier.toLowerCase());
}
/**
* Checks if the given string is a valid language identifier. Valid identifiers consist of only characters (a to z)
* and must be at least one character long
*
* @param identifier
* The string that should be tested for validity.
* @return true if the given string is a valid identifier, otherwise false.
*/
static boolean isValidIdentifier(String identifier) {
return IDENTIFIER_PATTERN.matcher(identifier).matches();
}
/**
* Either creates a new language with the given identifier and given dialect identifier or returns the already
* registered language object for the given identifiers. The identifiers will always be converted to lower-case and
* are therefore case-insensitive. Valid identifiers consist of only characters (a to z) and must be at least one
* character long.
*
* @param identifier
* The identifier for the language. It is recommended to use an ISO 639-1-compliant string. See format
* restrictions above.
* @param displayName
* The name of the language that will be displayed to the client. If the language is already cached, the
* registered displayname will be returned.
* @param dialectIdentifier
* The identifier for the dialect.
* @param dialectDisplayName
* The name of the dialect that will be displayed to the client. If the dialect is already cached, the
* registered dialect's displayname will be returned.
* @return A {@link Language} object with the given identifier.
*/
public static Language forSimpleLanguage(String identifier, String displayName, String dialectIdentifier, String dialectDisplayName) {
checkNotNull(identifier, "Language identifier may not be null");
checkNotNull(displayName, "Display name may not be null");
checkNotNull(dialectIdentifier, "Dialect identifier may not be null");
checkNotNull(dialectDisplayName, "Dialect display name may not be null");
checkArgument(isValidIdentifier(identifier), "Invalid language identifier: %s", identifier);
checkArgument(isValidIdentifier(dialectIdentifier), "Invalid dialect language identifier: %s", dialectIdentifier);
String key = identifier.toLowerCase() + "__" + dialectIdentifier;
dialectCache.putIfAbsent(key, new Language(identifier.toLowerCase(), displayName, dialectIdentifier, dialectDisplayName));
return dialectCache.get(key);
}
/**
* Returns the dialect's identifier of this language.
*
* @return the dialect's identifier of this language.
*/
@Nullable
public String getDialect() {
return this.dialect;
}
/**
* Returns the dialect's display name of this language (without main language). This is the value that should be
* displayed to a human. To get also the display name of the main you have to call {@link #getDisplayName()}.
*
* @return the dialect's display name of this language.
*/
@Nullable
public String getDialectDisplayName() {
return this.dialectDisplayName;
}
/**
* Returns the display name of this language (without the dialect). This is the value that should be displayed to a
* human user.
*
* @return the display name of this language.
*/
@NotNull
public String getDisplayName() {
return this.displayName;
}
/**
* Returns the identifier of this language (without the dialect). This method has to be unique per runtime and
* should only be used internally.
*
* @return the identifier of this language.
*/
@NotNull
public String getIdentifier() {
return this.identifier;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("identifier", this.identifier)
.add("dialect", this.dialect)
.toString();
}
}