/*
* IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
*
* http://izpack.org/
* http://izpack.codehaus.org/
*
* Licensed 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.
*/
package com.izforge.izpack;
import com.izforge.izpack.adaptator.IXMLElement;
import com.izforge.izpack.adaptator.IXMLParser;
import com.izforge.izpack.adaptator.impl.XMLParser;
import com.izforge.izpack.installer.ResourceManager;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;
/**
* Represents a database of a locale.
*
* @author Julien Ponge
* @author J. Chris Folsom <jchrisfolsom@gmail.com>
*/
public class LocaleDatabase extends TreeMap
{
/*
* Static cache of locale databases mapped by their iso name.
*/
private static Map<String, LocaleDatabase> cachedLocales = new HashMap<String, LocaleDatabase>();
/**
* The directory where language packs are kept inside the installer jar file.
*/
public static final String LOCALE_DATABASE_DIRECTORY = "/langpacks/";
/**
* The suffix for language pack definitions (.xml).
*/
public static final String LOCALE_DATABASE_DEF_SUFFIX = ".xml";
/*
* static character for replacing quotes
*/
private static final char TEMP_QUOTING_CHARACTER = '\uffff';
/**
* Load a locale database. If the database has already been loaded it will not be reloaded.
*
* @param isoCode The io code of the locale database.
*
* @return The locale database or null if it cannot be found.
*
* @throws Exception
*/
public static synchronized LocaleDatabase getLocaleDatabase(String isoCode) throws Exception
{
return getLocaleDatabase(isoCode, false);
}
/**
* Load a LocaleDatabase.
*
* @param isoCode The ISO language prefix for the locale.
* @param reload Whether or not to reload the locale database if it has already been loaded.
*
* @return The locale database or null if it cannot be found. <p/> FIXME Maybe we should define
* some custom exception like LocaleLoadException or something similar so that this class can
* have a method signature that does not throw Exception
*/
public static synchronized LocaleDatabase getLocaleDatabase(String isoCode, boolean reload)
throws Exception
{
LocaleDatabase langpack = cachedLocales.get(isoCode);
if (reload || langpack == null)
{
StringBuffer localeDefPath = new StringBuffer();
localeDefPath.append(LOCALE_DATABASE_DIRECTORY);
localeDefPath.append(isoCode);
localeDefPath.append(LOCALE_DATABASE_DEF_SUFFIX);
String path = localeDefPath.toString();
// The resource exists
if (LocaleDatabase.class.getResource(path) != null)
{
langpack = new LocaleDatabase(LocaleDatabase.class.getResourceAsStream(path));
cachedLocales.put(isoCode, langpack);
}
}
return langpack;
}
/**
* Load the current default LocaleDatabase.
*
* @throws Exception FIXME
*/
public static synchronized LocaleDatabase getLocaleDatabase() throws Exception
{
ResourceManager resourceManager = ResourceManager.getInstance();
String defaultLocale = resourceManager.getLocale();
return getLocaleDatabase(defaultLocale);
}
// End JCF changes
static final long serialVersionUID = 4941525634108401848L;
/**
* The constructor.
*
* @param in An InputStream to read the translation from.
*
* @throws Exception Description of the Exception
*/
public LocaleDatabase(InputStream in) throws Exception
{
// We call the superclass default constructor
super();
add(in);
}
/**
* Adds the contents of the given stream to the data base. The stream have to contain key value
* pairs as declared by the DTD langpack.dtd.
*
* @param in an InputStream to read the translation from.
*
* @throws Exception
*/
public void add(InputStream in) throws Exception
{
// Initialises the parser
IXMLParser parser = new XMLParser();
// We get the data
IXMLElement data = parser.parse(in);
// We check the data
if (!"langpack".equalsIgnoreCase(data.getName()))
{
throw new Exception(
"this is not an IzPack XML langpack file");
}
// We fill the Hashtable
Vector children = data.getChildren();
int size = children.size();
for (int i = 0; i < size; i++)
{
IXMLElement e = (IXMLElement) children.get(i);
String text = e.getContent();
if (text != null && !"".equals(text))
{
put(e.getAttribute("id"), text.trim());
}
else
{
put(e.getAttribute("id"), e.getAttribute("txt"));
}
}
}
/**
* Convenience method to retrieve an element.
*
* @param key The key of the element to retrieve.
*
* @return The element value or the key if not found.
*/
public String getString(String key)
{
String val = (String) get(key);
// At a change of the return value at val == null the method
// com.izforge.izpack.installer.IzPanel.getI18nStringForClass
// should be also addapted.
if (val == null)
{
val = key;
}
return val;
}
/**
* Convenience method to retrieve an element and simultaneously insert variables into the
* string. A place holder has to be build with the substring {n} where n is the parameter
* argument beginning with 0. The first argument is therefore {0}. If a parameter starts with a
* dollar sign the value will be used as key into the LocalDatabase. The key can be written as
* $MYKEY or ${MYKEY}. For all place holders an argument should be exist and vis a versa.
*
* @param key The key of the element to retrieve.
* @param variables the variables to insert
*
* @return The element value with the variables inserted or the key if not found.
*/
public String getString(String key, String[] variables)
{
for (int i = 0; i < variables.length; ++i)
{
if (variables[i] == null)
{
// The argument array with index is NULL! Replace it with N/A
variables[i] = "N/A";
}
else if (variables[i].startsWith("$"))
{ // Argument is also a key into the LocaleDatabase.
String curArg = variables[i];
if (curArg.startsWith("${"))
{
curArg = curArg.substring(2, curArg.length() - 1);
}
else
{
curArg = curArg.substring(1);
}
variables[i] = getString(curArg);
}
}
String message = getString(key);
// replace all ' characters because MessageFormat.format()
// don't substitute quoted place holders '{0}'
message = message.replace('\'', TEMP_QUOTING_CHARACTER);
message = MessageFormat.format(message, variables);
// replace all ' characters back
return message.replace(TEMP_QUOTING_CHARACTER, '\'');
}
/**
* Another method for retrieving an element. For those who don't want to
* define an array.
*
* @param object The object asking for the string.
* @param key The string it's asking.
* @param variables The optional variables to be incorporated in the string.
* @return The requested string, after the variables are inserted.
* @see #getString(String, String[])
* @author Alexis Wilhelm
* @since March 2009
*/
public String getString (Object object, String key, Object... variables)
{
key = object.getClass().getSimpleName() + "." + key;
String msg = getString(key);
if (msg == key) return key;
for (Object var: variables)
{
if (var == null)
{
var = "N/A";
}
else if (var instanceof String)
{
String str = (String) var;
if (str.startsWith("$"))
{
str = str.substring(1);
if (str.startsWith("{") && str.endsWith("}"))
{
str.substring(1, str.length() - 1);
}
var = getString(str);
}
}
}
msg = msg.replace('\'', TEMP_QUOTING_CHARACTER);
msg = MessageFormat.format(msg, variables);
return msg.replace(TEMP_QUOTING_CHARACTER, '\'');
}
}