Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2018 by Hitachi Vantara : 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 com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang.StringUtils;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Set;
public class GlobalMessageUtil {
private static final Logger log = LoggerFactory.getLogger( GlobalMessageUtil.class );
/**
* Used when the preferred locale (as defined by the user) is not available.
*/
public static final Locale FAILOVER_LOCALE = Locale.US;
protected static final LanguageChoice langChoice = LanguageChoice.getInstance();
protected static final ThreadLocal threadLocales = new ThreadLocal();
public static String formatErrorMessage( String key, String msg ) {
String s2 = key.substring( 0, key.indexOf( '.' ) + "ERROR_0000".length() + 1 );
return BaseMessages.getString( "MESSUTIL.ERROR_FORMAT_MASK", s2, msg );
}
private static String decorateMissingKey( final String key ) {
final StringBuilder keyBuilder = new StringBuilder();
keyBuilder.append( '!' ).append( key ).append( '!' );
return keyBuilder.toString();
}
public static String getString( ResourceBundle bundle, String key ) throws MissingResourceException {
return MessageFormat.format( bundle.getString( key ), new Object[] {} );
}
public static String getErrorString( ResourceBundle bundle, String key ) {
return formatErrorMessage( key, getString( bundle, key ) );
}
public static String getString( ResourceBundle bundle, String key, String param1 ) {
try {
Object[] args = { param1 };
return MessageFormat.format( bundle.getString( key ), args );
} catch ( Exception e ) {
return decorateMissingKey( key );
}
}
public static String getErrorString( ResourceBundle bundle, String key, String param1 ) {
return formatErrorMessage( key, getString( bundle, key, param1 ) );
}
public static String getString( ResourceBundle bundle, String key, String param1, String param2 ) {
try {
Object[] args = { param1, param2 };
return MessageFormat.format( bundle.getString( key ), args );
} catch ( Exception e ) {
return decorateMissingKey( key );
}
}
public static String getErrorString( ResourceBundle bundle, String key, String param1, String param2 ) {
return formatErrorMessage( key, getString( bundle, key, param1, param2 ) );
}
public static String getString( ResourceBundle bundle, String key, String param1, String param2, String param3 ) {
try {
Object[] args = { param1, param2, param3 };
return MessageFormat.format( bundle.getString( key ), args );
} catch ( Exception e ) {
return decorateMissingKey( key );
}
}
public static String getErrorString( ResourceBundle bundle, String key, String param1, String param2,
String param3 ) {
return formatErrorMessage( key, getString( bundle, key, param1, param2, param3 ) );
}
public static String getString( ResourceBundle bundle, String key, String param1, String param2, String param3,
String param4 ) {
try {
Object[] args = { param1, param2, param3, param4 };
return MessageFormat.format( bundle.getString( key ), args );
} catch ( Exception e ) {
return decorateMissingKey( key );
}
}
public static String getString( ResourceBundle bundle, String key, String param1, String param2, String param3,
String param4, String param5 ) {
try {
Object[] args = { param1, param2, param3, param4, param5 };
return MessageFormat.format( bundle.getString( key ), args );
} catch ( Exception e ) {
return decorateMissingKey( key );
}
}
public static String getString( ResourceBundle bundle, String key, String param1, String param2, String param3,
String param4, String param5, String param6 ) {
try {
Object[] args = { param1, param2, param3, param4, param5, param6 };
return MessageFormat.format( bundle.getString( key ), args );
} catch ( Exception e ) {
return decorateMissingKey( key );
}
}
public static String getErrorString( ResourceBundle bundle, String key, String param1, String param2,
String param3, String param4 ) {
return formatErrorMessage( key, getString( bundle, key, param1, param2, param3, param4 ) );
}
public static void setLocale( Locale newLocale ) {
threadLocales.set( newLocale );
}
public static Locale getLocale() {
Locale rtn = threadLocales.get();
if ( rtn != null ) {
return rtn;
}
setLocale( langChoice.getDefaultLocale() );
return langChoice.getDefaultLocale();
}
/**
* Returns a {@link LinkedHashSet} of {@link Locale}s for consideration when localizing text. The
* {@link LinkedHashSet} contains the user selected preferred {@link Locale}, the failover {@link Locale}
* ({@link Locale#ENGLISH}) and the {@link Locale#ROOT}.
*
* @return Returns a {@link LinkedHashSet} of {@link Locale}s for consideration when translating text
*/
public static LinkedHashSet getActiveLocales() {
// Use a LinkedHashSet to maintain order
final LinkedHashSet activeLocales = new LinkedHashSet<>();
// Example: messages_fr_FR.properties
activeLocales.add( langChoice.getDefaultLocale() );
// Example: messages_en_US.properties
activeLocales.add( FAILOVER_LOCALE );
// Example: messages.properties
activeLocales.add( Locale.ROOT );
return activeLocales;
}
/**
* Calls {@link #calculateString(String[], String, Object[], Class, String, boolean)} with the {@code
* logNotFoundError} parameter set to {@code true} to ensure proper error logging when the localized string cannot be
* found.
*/
public static String calculateString( final String[] pkgNames, final String key, final Object[] parameters,
final Class> resourceClass, final String bundleName ) {
return calculateString( pkgNames, key, parameters, resourceClass, bundleName, false, false );
}
public static String calculateString( final String[] pkgNames, final String key, Object[] parameters,
final Class> resourceClass, final String bundleName,
final boolean fallbackOnRoot ) {
return calculateString( pkgNames, key, parameters, resourceClass, bundleName, false, fallbackOnRoot );
}
/**
* Returns the localized string for the given {@code key} and {@code parameters} in a bundle defined by the the
* concatenation of the package names defined in {@code packageName} and @code bundleName} (the first valid
* combination of {@code packageName} + {@code bundleName} wins), sing the provided {@code resourceClass}'s class
* loader.
*
* @param pkgNames an array of packages potentially containing the localized messages the first one found to
* contain the messages is the one that is used to localize the message
* @param key the message key being looked up
* @param parameters parameters within the looked up message
* @param resourceClass the class whose class loader is used to getch the resource bundle
* @param bundleName the name of the message bundle
* @param logNotFoundError determines whether an error is logged when the localized string cannot be found - it can be
* used to suppress the log in cases where it is known that various combinations of parameters
* will be tried to fetch the message, to avoid unnecessary error logging.
* @param fallbackOnRoot if true, and a {@link ResourceBundle} cannot be found for a given {@link Locale},
* falls back on the ROOT {@link Locale}
* @return the localized string for the given {@code key} and {@code parameters} in a bundle defined by the the
* concatenation of the package names defined in {@code packageName} and @code bundleName} (the first valid
* combination of {@code packageName} + @code bundleName} wins), sing the provided {@code resourceClass}'s class
* loader
*/
public static String calculateString( final String[] pkgNames, final String key, final Object[] parameters,
final Class> resourceClass, final String bundleName,
final boolean logNotFoundError, final boolean fallbackOnRoot ) {
final Set activeLocales = getActiveLocales();
for ( final Locale locale : activeLocales ) {
final String string =
calculateString( pkgNames, locale, key, parameters, resourceClass, bundleName, fallbackOnRoot );
if ( !isMissingKey( string ) ) {
return string;
}
}
if ( logNotFoundError ) {
final StringBuilder msg = new StringBuilder();
msg.append( "Message not found in the preferred and failover locale: key=[" ).append( key ).append(
"], package=" ).append( Arrays.asList( pkgNames ) );
log.error( Const.getStackTracker( new KettleException( msg.toString() ) ) );
}
return decorateMissingKey( key );
}
private static String calculateString( final String[] pkgNames, final Locale locale, final String key,
final Object[] parameters, final Class> resourceClass,
final String bundleName, final boolean fallbackOnRoot ) {
for ( final String packageName : pkgNames ) {
try {
return calculateString( packageName, locale, key, parameters, resourceClass, bundleName, fallbackOnRoot );
} catch ( final MissingResourceException e ) {
continue;
}
}
return null;
}
static String calculateString( final String packageName, final Locale locale, final String key, Object[] parameters,
final Class> resourceClass, final String bundleName ) {
return calculateString( packageName, locale, key, parameters, resourceClass, bundleName, true );
}
@VisibleForTesting
static String calculateString( final String packageName, final Locale locale, final String key,
final Object[] parameters, final Class> resourceClass, final String bundleName,
final boolean fallbackOnRoot ) throws MissingResourceException {
try {
ResourceBundle bundle = getBundle( locale, packageName + "." + bundleName, resourceClass, fallbackOnRoot );
String unformattedString = bundle.getString( key );
String string = MessageFormat.format( unformattedString, parameters );
return string;
} catch ( IllegalArgumentException e ) {
final StringBuilder msg = new StringBuilder();
msg.append( "Format problem with key=[" ).append( key ).append( "], locale=[" ).append( locale ).append(
"], package=" ).append( packageName ).append( " : " ).append( e.toString() );
log.error( msg.toString() );
log.error( Const.getStackTracker( e ) );
throw new MissingResourceException( msg.toString(), packageName, key );
}
}
/**
* Retrieve a resource bundle of the default or fail-over locales.
*
* @param packagePath 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( final String packagePath, final Class> resourceClass )
throws MissingResourceException {
final Set activeLocales = getActiveLocales();
for ( final Locale locale : activeLocales ) {
try {
return getBundle( locale, packagePath, resourceClass );
} catch ( MissingResourceException e ) {
final StringBuilder msg = new StringBuilder();
msg.append( "Unable to find properties file for package '" ).append( packagePath ).append( "' and class '" )
.append( resourceClass.getName() ).append( "' in the available locales: " ).append( locale );
// nothing to do, an exception will be thrown if no bundle is found
log.warn( msg.toString() );
}
}
final StringBuilder msg = new StringBuilder();
msg.append( "Unable to find properties file for package '" ).append( packagePath ).append( "' and class '" )
.append( resourceClass.getName() ).append( "' in the available locales: " ).append(
Arrays.asList( activeLocales ) );
throw new MissingResourceException( msg.toString(), resourceClass.getName(),
packagePath );
}
public static ResourceBundle getBundle( Locale locale, String packagePath, Class> resourceClass ) {
return getBundle( locale, packagePath, resourceClass, true );
}
/**
* Returns a {@link ResourceBundle} corresponding to the given {@link Locale} package and resource class. Falls-back
* on the ROOT {@link Locale}, if the {@code fallbackOnRoot} flag is true and the requested Locale is not available.
*
* @param locale the {@link Locale} for which the {@link ResourceBundle} is being requested
* @param packagePath
* @param resourceClass
* @param fallbackOnRoot if true, and a {@link ResourceBundle} cannot be found for the requested {@link Locale}, falls
* back on the ROOT {@link Locale}
* @return a {@link ResourceBundle} corresponding to the given {@link Locale} package and resource class
*/
public static ResourceBundle getBundle( final Locale locale, final String packagePath, final Class> resourceClass,
final boolean fallbackOnRoot ) {
final GlobalMessageControl control = new GlobalMessageControl( fallbackOnRoot );
final String resourceName = control.toResourceName( control.toBundleName( packagePath, locale ), "properties" );
ResourceBundle bundle;
try {
bundle = ResourceBundle.getBundle( packagePath, locale, resourceClass.getClassLoader(),
new GlobalMessageControl( fallbackOnRoot ) );
} catch ( final MissingResourceException e ) {
final StringBuilder msg = new StringBuilder();
msg.append( "Unable to find properties file '" ).append( resourceName ).append( "': " ).append( e.toString() );
throw new MissingResourceException( msg.toString(), resourceClass.getName(), packagePath );
}
return bundle;
}
/**
* Returns a string corresponding to the locale (Example: "en", "en_US").
*
* @param locale The {@link Locale} whose string representation it being returned
* @return a string corresponding to the locale (Example: "en", "en_US").
*/
protected static String getLocaleString( Locale locale ) {
final StringBuilder localeString = new StringBuilder();
if ( locale != null && !StringUtils.isBlank( locale.getLanguage() ) ) {
if ( !StringUtils.isBlank( locale.getCountry() ) ) {
// force language to be lower case and country to be upper case
localeString.append( locale.getLanguage().toLowerCase() ).append( '_' ).append(
locale.getCountry().toUpperCase() );
} else {
// force language to be lower case
localeString.append( locale.getLanguage().toLowerCase() );
}
}
return localeString.toString();
}
/**
* Returns true if the given {@code string} is null or is in the format of a missing key: !key!.
*
* @param string
* @return true if the given {@code string} is null or is in the format of a missing key: !key!.
*/
protected static boolean isMissingKey( final String string ) {
return string == null || ( string.trim().startsWith( "!" ) && string.trim().endsWith( "!" )
&& !string.trim().equals( "!" ) );
}
/**
* A custom {@link ResourceBundle.Control} implementation that provides the desired fall-back mechanism.
*/
static class GlobalMessageControl extends ResourceBundle.Control {
private boolean fallbackOnRoot;
GlobalMessageControl( final boolean fallbackOnRoot ) {
this.fallbackOnRoot = fallbackOnRoot;
}
@Override
public Locale getFallbackLocale( final String baseName, final Locale locale ) {
// we have our own fall-back mechanism
return null;
}
@Override
public List getCandidateLocales( final String baseName, final Locale locale ) {
// we have our own fall-back mechanism
final List locales = super.getCandidateLocales( baseName, locale );
// remove the root locale, as we want to handle it ourselves, unless the locale itself is root
if ( !fallbackOnRoot && !locale.equals( Locale.ROOT ) ) {
locales.remove( Locale.ROOT );
}
return locales;
}
@Override
public ResourceBundle newBundle( final String baseName, final Locale locale, final String format,
final ClassLoader loader, final boolean reload )
throws IllegalAccessException, InstantiationException, IOException {
final String resourceName = toResourceName( toBundleName( baseName, locale ), "properties" );
ResourceBundle bundle;
InputStream stream = null;
if ( reload ) {
final URL url = loader.getResource( resourceName );
if ( url != null ) {
final URLConnection connection = url.openConnection();
if ( connection != null ) {
connection.setUseCaches( false );
stream = connection.getInputStream();
}
}
} else {
stream = loader.getResourceAsStream( resourceName );
}
if ( stream == null ) {
// Retry with the system class loader, just in case we are dealing with a messy plug-in.
stream = ClassLoader.getSystemResourceAsStream( resourceName );
}
if ( stream != null ) {
try {
// use UTF-8 encoding
bundle = new PropertyResourceBundle( new InputStreamReader( stream, StandardCharsets.UTF_8.name() ) );
} finally {
stream.close();
}
} else {
final StringBuilder msg = new StringBuilder();
msg.append( "Unable to find properties file '" ).append( resourceName ).append( "'" );
throw new MissingResourceException( msg.toString(), loader.getClass().getName(), baseName );
}
return bundle;
}
}
}