/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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 org.pentaho.di.i18n;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.io.InputStreamReader;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
public class GlobalMessages extends AbstractMessageHandler {
private static String packageNM = GlobalMessages.class.getPackage().getName();
protected static final ThreadLocal<Locale> threadLocales = new ThreadLocal<Locale>();
protected static final LanguageChoice langChoice = LanguageChoice.getInstance();
protected static final String SYSTEM_BUNDLE_PACKAGE = GlobalMessages.class.getPackage().getName();
protected static final String BUNDLE_NAME = "messages.messages";
protected static final Map<String, ResourceBundle> locales = Collections
.synchronizedMap( new HashMap<String, ResourceBundle>() );
protected static final LogChannelInterface log = new LogChannel( "i18n" );
public static final String[] localeCodes = {
"en_US", "nl_NL", "zh_CN", "es_ES", "fr_FR", "de_DE", "pt_BR", "pt_PT", "es_AR", "no_NO", "it_IT", "ja_JP",
"ko_KR" };
public static final String[] localeDescr = {
"English (US)", "Nederlands", "Simplified Chinese", "Espa\u00F1ol (Spain)", "Fran\u00E7ais", "Deutsch",
"Portuguese (Brazil)", "Portuguese (Portugal)", "Espa\u00F1ol (Argentina)", "Norwegian (Norway)",
"Italian (Italy)", "Japanese (Japan)", "Korean (Korea)", };
protected static GlobalMessages GMinstance = null;
/**
* TODO: extend from abstract class to ensure singleton status and migrate instantiation to class controlled private
*/
public GlobalMessages() {
}
public static synchronized MessageHandler getInstance() {
if ( GMinstance == null ) {
GMinstance = new GlobalMessages();
}
return GMinstance;
}
protected static Map<String, ResourceBundle> getLocales() {
return locales;
}
public static synchronized Locale getLocale() {
Locale rtn = threadLocales.get();
if ( rtn != null ) {
return rtn;
}
setLocale( langChoice.getDefaultLocale() );
return langChoice.getDefaultLocale();
}
public static synchronized void setLocale( Locale newLocale ) {
threadLocales.set( newLocale );
}
protected static String getLocaleString( Locale locale ) {
String locString = locale.toString();
if ( locString.length() == 5 && locString.charAt( 2 ) == '_' ) { // Force upper-lowercase format
locString = locString.substring( 0, 2 ).toLowerCase() + "_" + locString.substring( 3 ).toUpperCase();
// System.out.println("locString="+locString);
}
return locString;
}
protected static String buildHashKey( Locale locale, String packageName ) {
return packageName + "_" + getLocaleString( locale );
}
protected static String buildBundleName( String packageName ) {
return packageName + "." + BUNDLE_NAME;
}
/**
* Retrieve a resource bundle of the default or fail-over locale.
*
* @param packageName
* The package to search in
* @return The resource bundle
* @throws MissingResourceException
* in case both resource bundles couldn't be found.
*/
public static ResourceBundle getBundle( String packageName ) throws MissingResourceException {
return getBundle( packageName, GlobalMessages.getInstance().getClass() );
}
/**
* Retrieve a resource bundle of the default or fail-over locale.
*
* @param packageName
* The package to search in
* @param resourceClass
* the class to use to resolve the bundle
* @return The resource bundle
* @throws MissingResourceException
* in case both resource bundles couldn't be found.
*/
public static ResourceBundle getBundle( String packageName, Class<?> resourceClass ) throws MissingResourceException {
ResourceBundle bundle;
try {
// First try to load the bundle in the default locale
//
bundle = getBundle( LanguageChoice.getInstance().getDefaultLocale(), packageName, resourceClass );
return bundle;
} catch ( MissingResourceException e ) {
try {
// Now retry the fail-over locale (en_US etc)
//
bundle = getBundle( LanguageChoice.getInstance().getFailoverLocale(), packageName, resourceClass );
return bundle;
} catch ( MissingResourceException e2 ) {
// If nothing usable could be found throw an exception...
//
throw new MissingResourceException( "Unable to find properties file in the default '"
+ LanguageChoice.getInstance().getDefaultLocale() + "' nor the failore locale '"
+ LanguageChoice.getInstance().getFailoverLocale() + "'", packageName, packageName );
}
}
}
public static ResourceBundle getBundle( Locale locale, String packageName ) throws MissingResourceException {
return getBundle( locale, packageName, GlobalMessages.getInstance().getClass() );
}
public static ResourceBundle getBundle( Locale locale, String packageName, Class<?> resourceClass ) throws MissingResourceException {
String filename = buildHashKey( locale, packageName );
filename = "/" + filename.replace( '.', '/' ) + ".properties";
InputStream inputStream = null;
try {
ResourceBundle bundle = locales.get( filename );
if ( bundle == null ) {
inputStream = resourceClass.getResourceAsStream( filename );
if ( inputStream == null ) {
// Retry with the system class loader, just in case we are dealing with a messy plug-in.
//
inputStream = ClassLoader.getSystemResourceAsStream( filename );
}
// Now get the bundle from the messages files input stream
//
if ( inputStream != null ) {
bundle = new PropertyResourceBundle( new InputStreamReader( inputStream, "UTF-8" ) );
locales.put( filename, bundle );
} else {
throw new MissingResourceException( "Unable to find properties file [" + filename + "]", locale
.toString(), packageName );
}
}
return bundle;
} catch ( IOException e ) {
throw new MissingResourceException(
"Unable to find properties file [" + filename + "] : " + e.toString(), locale.toString(), packageName );
} finally {
if ( inputStream != null ) {
try {
inputStream.close();
} catch ( Exception e ) {
// ignore this
}
}
}
}
protected String findString( String packageName, Locale locale, String key, Object[] parameters ) throws MissingResourceException {
return findString( packageName, locale, key, parameters, GlobalMessages.getInstance().getClass() );
}
protected String findString( String packageName, Locale locale, String key, Object[] parameters,
Class<?> resourceClass ) throws MissingResourceException {
try {
ResourceBundle bundle = getBundle( locale, packageName + "." + BUNDLE_NAME, resourceClass );
String unformattedString = bundle.getString( key );
String string = MessageFormat.format( unformattedString, parameters );
return string;
} catch ( IllegalArgumentException e ) {
String message =
"Format problem with key=["
+ key + "], locale=[" + locale + "], package=" + packageName + " : " + e.toString();
log.logError( message );
log.logError( Const.getStackTracker( e ) );
throw new MissingResourceException( message, packageName, key );
}
}
protected String calculateString( String packageName, String key, Object[] parameters ) {
return calculateString( packageName, key, parameters, GlobalMessages.getInstance().getClass() );
}
protected String calculateString( String packageName, String key, Object[] parameters, Class<?> resourceClass ) {
String string = null;
// First try the standard locale, in the local package
try {
string = findString( packageName, langChoice.getDefaultLocale(), key, parameters, resourceClass );
} catch ( MissingResourceException e ) { /* Ignore */
}
if ( string != null ) {
return string;
}
// Then try to find it in the i18n package, in the system messages of the preferred language.
try {
string = findString( SYSTEM_BUNDLE_PACKAGE, langChoice.getDefaultLocale(), key, parameters, resourceClass );
} catch ( MissingResourceException e ) { /* Ignore */
}
if ( string != null ) {
return string;
}
// Then try the failover locale, in the local package
try {
string = findString( packageName, langChoice.getFailoverLocale(), key, parameters, resourceClass );
} catch ( MissingResourceException e ) { /* Ignore */
}
if ( string != null ) {
return string;
}
// Then try to find it in the i18n package, in the system messages of the failover language.
try {
string = findString( SYSTEM_BUNDLE_PACKAGE, langChoice.getFailoverLocale(), key, parameters, resourceClass );
} catch ( MissingResourceException e ) { /* Ignore */
}
if ( string != null ) {
return string;
}
string = "!" + key + "!";
String message =
"Message not found in the preferred and failover locale: key=[" + key + "], package=" + packageName;
log.logDetailed( Const.getStackTracker( new KettleException( message ) ) );
return string;
}
@Override
public String getString( String key ) {
Object[] parameters = null;
return calculateString( packageNM, key, parameters );
}
@Override
public String getString( String packageName, String key ) {
Object[] parameters = new Object[] {};
return calculateString( packageName, key, parameters );
}
@Override
public String getString( String packageName, String key, String... parameters ) {
return calculateString( packageName, key, parameters );
}
@Override
public String getString( String packageName, String key, Class<?> resourceClass, String... parameters ) {
return calculateString( packageName, key, parameters, resourceClass );
}
}