//----------------------------------------------------------------------------//
// //
// L a n g u a g e //
// //
//----------------------------------------------------------------------------//
// <editor-fold defaultstate="collapsed" desc="hdr"> //
// Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. //
// This software is released under the GNU General Public License. //
// Goto http://kenai.com/projects/audiveris to report bugs or suggestions. //
//----------------------------------------------------------------------------//
// </editor-fold>
package omr.text;
import omr.WellKnowns;
import omr.constant.Constant;
import omr.constant.ConstantSet;
import omr.util.Param;
import omr.util.UriUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.swing.AbstractListModel;
/**
* Class {@code Language} handles the collection of language codes
* with their related full name, as well as the default language
* specification.
*
* <p>A language specification specifies a list of languages.
* It is a string formatted as LAN[+LAN]*
*
* <p>Note: languages are implemented as a (sorted) map, since a compiled enum
* would not provide the ability to add new items at run time.
*
* @author Hervé Bitteur
*/
public class Language
{
//~ Static fields/initializers ---------------------------------------------
/** Specific application parameters */
private static final Constants constants = new Constants();
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
Language.class);
/** Languages file name. */
private static final String LANG_FILE_NAME = "ISO639-3.xml";
/** Separator in a specification. */
public static final String SEP_CHAR = "+";
/** Default language specification (such as ENG+DEU+ITA). */
public static final Param<String> defaultSpecification = new Default();
/** Language used when specification is empty. */
private static final String NO_SPEC = "eng";
/** Collection of supported languages, lazily created. */
private static SupportedLanguages supportedLanguages;
//~ Constructors -----------------------------------------------------------
//
//----------//
// Language //
//----------//
/** Not meant to be instantiated */
private Language ()
{
}
//~ Methods ----------------------------------------------------------------
//-----------------------//
// getSupportedLanguages //
//-----------------------//
private static SupportedLanguages getSupportedLanguages ()
{
if (supportedLanguages == null) {
supportedLanguages = new SupportedLanguages();
}
return supportedLanguages;
}
//~ Inner Classes ----------------------------------------------------------
//-----------//
// ListModel //
//-----------//
/**
* A JList model to support manual handling of language codes.
*/
public static class ListModel
extends AbstractListModel<String>
{
//~ Constructors -------------------------------------------------------
public ListModel ()
{
}
//~ Methods ------------------------------------------------------------
@Override
public String getElementAt (int index)
{
return getSupportedLanguages()
.getElementAt(index);
}
@Override
public int getSize ()
{
return getSupportedLanguages()
.getSize();
}
/**
* Report the array of indices for the spec codes.
*
* @param spec the provided spec
* @return the array of indices in the model.
* If the spec is empty, an empty int array is returned
*/
public int[] indicesOf (String spec)
{
return getSupportedLanguages()
.indicesOf(spec);
}
/**
* Build the spec string out of the provided sequence of codes.
*
* @param list the provided codes
* @return the resulting specification string.
* The "eng" string is returned if the provided list is empty.
*/
public String specOf (Collection<String> list)
{
return getSupportedLanguages()
.specOf(list);
}
}
//
//-----------//
// Constants //
//-----------//
private static final class Constants
extends ConstantSet
{
//~ Instance fields ----------------------------------------------------
Constant.String defaultSpecification = new Constant.String(
NO_SPEC,
"List of 3-letter codes, separated by '+'");
}
//---------//
// Default //
//---------//
private static class Default
extends Param<String>
{
//~ Methods ------------------------------------------------------------
@Override
public String getSpecific ()
{
return constants.defaultSpecification.getValue();
}
@Override
public boolean setSpecific (String specific)
{
if (!getSpecific()
.equals(specific)) {
constants.defaultSpecification.setValue(specific);
logger.info(
"Default language specification is now ''{}''",
specific);
return true;
}
return false;
}
}
//--------------------//
// SupportedLanguages //
//--------------------//
/**
* Handles the collection of supported languages.
*/
private static class SupportedLanguages
{
//~ Instance fields ----------------------------------------------------
/** Map of language code -> language full name. */
private final SortedMap<String, String> codes = new TreeMap<String, String>();
/** Convenient sequence of codes, parallel to sorted map. */
private final List<String> codesList;
//~ Constructors -------------------------------------------------------
public SupportedLanguages ()
{
// Build the map of all possible codes
Properties langNames = new Properties();
URI uri = UriUtil.toURI(WellKnowns.RES_URI, LANG_FILE_NAME);
try (InputStream input = uri.toURL().openStream()) {
langNames.loadFromXML(input);
for (String code : langNames.stringPropertyNames()) {
codes.put(code, langNames.getProperty(code, code));
}
} catch (Throwable ex) {
logger.error("Error loading " + uri, ex);
}
// Now, keep only the supported codes
// TODO: Protect against no OCR!
Set<String> supported = TextBuilder.getOcr()
.getLanguages();
codes.keySet()
.retainAll(supported);
// Create parallel list of codes
codesList = new ArrayList<String>(codes.keySet());
}
//~ Methods ------------------------------------------------------------
/**
* Report the code out of a list item.
*
* @param item the list item, such as "eng (English)"
* @return the code, such as "eng"
*/
public String codeOf (String item)
{
return item.substring(0, 3);
}
/**
* Report a string built as: "code (full name)".
*
* @param code provided code, such as "fra"
* @return the related string, such as "fra (French)"
*/
public String getElementAt (int index)
{
final String code = codesList.get(index);
final String fullName = nameOf(code);
if (fullName != null) {
return code + " (" + fullName + ")";
} else {
return code;
}
}
public int getSize ()
{
return codesList.size();
}
public int[] indicesOf (String spec)
{
if (spec.trim()
.isEmpty()) {
return new int[0];
} else {
List<String> list = codesOf(spec);
int[] ints = new int[list.size()];
for (int i = 0; i < ints.length; i++) {
String code = list.get(i);
ints[i] = codesList.indexOf(code);
}
return ints;
}
}
public String specOf (Collection<String> list)
{
StringBuilder sb = new StringBuilder();
for (String item : list) {
if (sb.length() > 0) {
sb.append(SEP_CHAR);
}
sb.append(codeOf(item));
}
if (sb.length() > 0) {
return sb.toString();
} else {
return NO_SPEC;
}
}
/**
* Convert a language specification (DEU+FRA+ITA) to a sequence
* of codes [DEU, FRA, ITA].
*
* @param spec the language specification to parse
* @return the sequence of codes.
*/
private List<String> codesOf (String spec)
{
final String[] tokens = spec.split("\\" + SEP_CHAR);
return Arrays.asList(tokens);
}
/**
* Report the full language name mapped to a language code.
*
* @param code the language code, such as "eng"
* @return the corresponding language full name, such as "English",
* or null if unknown
*/
private String nameOf (String code)
{
return codes.get(code);
}
}
}