All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.numdata.oss.ResourceBundleTools Maven / Gradle / Ivy

There is a newer version: 1.22
Show newest version
/*
 * Copyright (c) 2017, Numdata BV, The Netherlands.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Numdata nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NUMDATA BV BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.numdata.oss;

import java.io.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;

import com.numdata.oss.log.*;
import org.jetbrains.annotations.*;

/**
 * This utility class contains utility methods to access resource bundles.
 *
 * @author Peter S. Heijnen
 */
public class ResourceBundleTools
{
	/**
	 * Log used for messages related to this class.
	 */
	private static final ClassLogger LOG = ClassLogger.getFor( ResourceBundleTools.class );

	/**
	 * This is used to support pre-loaded resource bundle data.
	 */
	@SuppressWarnings( "ConstantNamingConvention" )
	private static final ResourcePack _resourceData;

	/**
	 * Recursive bundle cache.
	 */
	@SuppressWarnings( "ConstantNamingConvention" )
	private static final Map> _recursiveBundleCache = new HashMap>();

	/*
	 * Try to load resource data from 'res.dat' resource. This should be as
	 * forgiving to errors as possible.
	 */
	static
	{
		ResourcePack result = null;
		try
		{
			final ClassLoader classLoader = ResourceBundleTools.class.getClassLoader();

			final InputStream is = classLoader.getResourceAsStream( "res.dat" );
			try
			{
				if ( is != null )
				{
					result = new ResourcePack( new DataInputStream( new GZIPInputStream( is ) ) );
				}
			}
			finally
			{
				if ( is != null )
				{
					is.close();
				}
			}
		}
		catch ( final Throwable ignored )
		{
		}
		_resourceData = result;
	}

	/**
	 * Utility class is not to be instantiated.
	 */
	private ResourceBundleTools()
	{
	}

	/**
	 * Recursive bundle cache.
	 */
	@SuppressWarnings( "ConstantNamingConvention" )
	public void clearCache()
	{
		final Map> recursiveBundleCache = _recursiveBundleCache;
		//noinspection SynchronizationOnLocalVariableOrMethodParameter
		synchronized ( recursiveBundleCache )
		{
			recursiveBundleCache.clear();
		}
	}

	/**
	 * Get resource bundle for the specified class hierarchy and locale. If no
	 * locale is specified, the default locale is used.
	 *
	 * This method gets a bundle that contains resources for the specified class
	 * and all its parent classes (if any of them have bundles).
	 *
	 * @param clazz  Class to get the bundle for.
	 * @param locale Locale to get the bundle for.
	 *
	 * @return ResourceBundle for specified source and locale.
	 *
	 * @throws MissingResourceException if no resource bundle for the specified
	 * class can be found.
	 */
	@NotNull
	@SuppressWarnings( { "CallToNativeMethodWhileLocked", "ObjectEquality" } )
	public static ResourceBundle getBundleHierarchy( @NotNull final Class clazz, @Nullable final Locale locale )
	{
		final boolean trace = LOG.isTraceEnabled();
		final boolean debug = trace || LOG.isDebugEnabled();
		if ( debug )
		{
			LOG.debug( "getBundleHierarchy( " + clazz + ", " + locale + " )" );
		}
		final ResourceBundle result;

		final String className = clazz.getName();

		final Locale usedLocale = ( locale == null ) ? Locale.getDefault() : locale;

		final Map> recursiveBundleCache = _recursiveBundleCache;
		//noinspection SynchronizationOnLocalVariableOrMethodParameter
		synchronized ( recursiveBundleCache )
		{
			Map localeCache = recursiveBundleCache.get( usedLocale );
			if ( localeCache == null )
			{
				recursiveBundleCache.put( usedLocale, localeCache = new HashMap( 1 ) );
			}

			if ( localeCache.containsKey( className ) )
			{
				result = localeCache.get( className );
			}
			else
			{
				final List bundles = new ArrayList();

				final Class superclass = clazz.getSuperclass();
				ResourceBundle superBundle = null;
				if ( ( superclass != null ) && ( superclass != Object.class ) )
				{
					try
					{
						superBundle = getBundleHierarchy( superclass, usedLocale );
						if ( trace )
						{
							LOG.trace( "getBundleHierarchy() got super-bundle for super-class " + superclass.getName() + " with keys " + new TreeSet( superBundle.keySet() ) );
						}
						bundles.add( superBundle );
					}
					catch ( final MissingResourceException ignored )
					{
					}
				}

				final Class declaringClass = getDeclaringClass( clazz );
				if ( declaringClass != null )
				{
					try
					{
						final ResourceBundle bundle = getBundleHierarchy( declaringClass, usedLocale );
						bundles.add( bundle );
						if ( trace )
						{
							LOG.trace( "getBundleHierarchy() got bundle for declaring class " + declaringClass.getName() + " with keys " + new TreeSet( bundle.keySet() ) );
						}
					}
					catch ( final MissingResourceException ignored )
					{
					}
				}

				final Package localPackage = clazz.getPackage();
				if ( trace )
				{
					LOG.trace( "getBundleHierarchy() localPackage=" + localPackage );
				}
				if ( ( localPackage != null ) && ( ( superBundle == null ) || !localPackage.equals( superclass.getPackage() ) ) )
				{
					final int packageBundleIndex = bundles.size();
					String packageName = localPackage.getName();
					do
					{
						final String packageBundleName = packageName + ".LocalStrings";
						if ( trace )
						{
							LOG.trace( "getBundleHierarchy() packageBundleName=" + packageBundleName );
						}
						try
						{
							final ResourceBundle bundle = getBundle( packageBundleName, usedLocale, clazz.getClassLoader() );
							bundles.add( packageBundleIndex, bundle );
							if ( trace )
							{
								LOG.trace( "getBundleHierarchy() got bundle for package " + packageName + " with keys " + new TreeSet( bundle.keySet() ) );
							}
						}
						catch ( final MissingResourceException ignored )
						{
						}

						final int dot = packageName.lastIndexOf( '.' );
						packageName = ( dot > 0 ) ? packageName.substring( 0, dot ) : null;
					}
					while ( packageName != null );
				}

				try
				{
					final ResourceBundle bundle = getBundle( clazz, usedLocale );
					bundles.add( bundle );
					if ( trace )
					{
						LOG.trace( "getBundleHierarchy() got bundle for requested class " + clazz.getName() + " with keys " + new TreeSet( bundle.keySet() ) );
					}
				}
				catch ( final MissingResourceException ignored )
				{
				}

				if ( trace )
				{
					LOG.trace( "getBundleHierarchy() combine " + bundles.size() + " bundle(s) for class  " + clazz.getName() );
				}

				if ( bundles.isEmpty() )
				{
					result = null;
				}
				else if ( bundles.size() == 1 )
				{
					result = bundles.get( 0 );
				}
				else // bundles.size() > 1
				{
					final MergedResourceBundle mergedBundle = new MergedResourceBundle( usedLocale );
					for ( final ResourceBundle bundle : bundles )
					{
						mergedBundle.addBundle( bundle );

					}
					result = mergedBundle;
				}
				if ( trace )
				{
					LOG.trace( "getBundleHierarchy() result for " + clazz.getName() + " => " + ( ( result != null ) ? "bundle with keys " + new TreeSet( result.keySet() ) : "null" ) );
				}

				localeCache.put( className, result );
			}
		}

		if ( result == null )
		{
			throw new MissingResourceException( className, className, null );
		}

		return result;
	}

	/**
	 * Returns which bundles would be included by {@link #getBundleHierarchy}.
	 *
	 * @param clazz Class to get the bundle hierarchy for.
	 *
	 * @return Bundle names included in the bundle hierarchy.
	 */
	@NotNull
	@SuppressWarnings( { "ObjectEquality", "unused", "WeakerAccess" } )
	public static List getBundleHierarchyNames( @NotNull final Class clazz )
	{
		final boolean trace = LOG.isTraceEnabled();
		final boolean debug = trace || LOG.isDebugEnabled();
		if ( debug )
		{
			LOG.debug( "getBundleHierarchyElements( " + clazz + " )" );
		}

		final List result = new ArrayList();

		final Class superclass = clazz.getSuperclass();
		ResourceBundle superBundle = null;
		if ( ( superclass != null ) && ( superclass != Object.class ) )
		{
			try
			{
				superBundle = getBundleHierarchy( superclass, null );
				if ( trace )
				{
					LOG.trace( "getBundleHierarchyElements() got super-bundle for super-class " + superclass.getName() + " with keys " + new TreeSet( superBundle.keySet() ) );
				}
				result.addAll( getBundleHierarchyNames( superclass ) );
			}
			catch ( final MissingResourceException ignored )
			{
			}
		}

		final Class declaringClass = getDeclaringClass( clazz );
		if ( declaringClass != null )
		{
			try
			{
				final ResourceBundle bundle = getBundleHierarchy( declaringClass, null );
				if ( trace )
				{
					LOG.trace( "getBundleHierarchyElements() got bundle for declaring class " + declaringClass.getName() + " with keys " + new TreeSet( bundle.keySet() ) );
				}
				result.addAll( getBundleHierarchyNames( declaringClass ) );
			}
			catch ( final MissingResourceException ignored )
			{
			}
		}

		final Package localPackage = clazz.getPackage();
		if ( trace )
		{
			LOG.trace( "getBundleHierarchyElements() localPackage=" + localPackage );
		}
		if ( ( localPackage != null ) && ( ( superBundle == null ) || !localPackage.equals( superclass.getPackage() ) ) )
		{
			String packageName = localPackage.getName();
			do
			{
				final String packageBundleName = packageName + ".LocalStrings";
				if ( trace )
				{
					LOG.trace( "getBundleHierarchyElements() packageBundleName=" + packageBundleName );
				}
				try
				{
					final ResourceBundle bundle = getBundle( packageBundleName, null, clazz.getClassLoader() );
					result.add( packageBundleName );
					if ( trace )
					{
						LOG.trace( "getBundleHierarchyElements() got bundle for package " + packageName + " with keys " + new TreeSet( bundle.keySet() ) );
					}
				}
				catch ( final MissingResourceException ignored )
				{
				}

				final int dot = packageName.lastIndexOf( '.' );
				packageName = ( dot > 0 ) ? packageName.substring( 0, dot ) : null;
			}
			while ( packageName != null );
		}

		try
		{
			final ResourceBundle bundle = getBundle( clazz, null );
			result.add( clazz.getName() );
			if ( trace )
			{
				LOG.trace( "getBundleHierarchyElements() got bundle for requested class " + clazz.getName() + " with keys " + new TreeSet( bundle.keySet() ) );
			}
		}
		catch ( final MissingResourceException ignored )
		{
		}

		if ( trace )
		{
			LOG.trace( "getBundleHierarchyElements() are " + result + " for class  " + clazz.getName() );
		}

		return result;
	}

	@SuppressWarnings( "JavaDoc" )
	@Nullable
	private static Class getDeclaringClass( @NotNull final Class clazz )
	{
		Class result = clazz.getDeclaringClass();
		if ( result == null )
		{
			final String className = clazz.getName();
			final int pos = className.indexOf( '$', className.lastIndexOf( '.' ) + 1 );
			if ( pos >= 0 )
			{
				try
				{
					final String outerClassName = className.substring( 0, pos );
					result = Class.forName( outerClassName );
				}
				catch ( final Throwable t )
				{
					/* did not work, too bad! */
				}
			}
		}

		//noinspection ObjectEquality
		if ( result == Object.class )
		{
			result = null;
		}

		if ( LOG.isTraceEnabled() )
		{
			LOG.trace( "getDeclaringClass() => " + result );
		}


		return result;
	}

	/**
	 * Get resource bundle for the specified class and locale.  If no locale is
	 * specified, the default locale is used.
	 *
	 * This method provides a central point for loading class resource bundles.
	 * It provides a hook for changing the default load behavior of bundles.
	 *
	 * @param clazz  Class to get the bundle for.
	 * @param locale Locale to get the bundle for.
	 *
	 * @return ResourceBundle for specified source and locale.
	 *
	 * @throws MissingResourceException if no resource bundle for the specified
	 * class can be found.
	 */
	@NotNull
	public static ResourceBundle getBundle( @NotNull final Class clazz, @Nullable final Locale locale )
	{
		return getBundle( clazz.getName(), locale, clazz.getClassLoader() );
	}

	/**
	 * Get resource bundle for the specified class and locale.  If no locale is
	 * specified, the default locale is used.
	 *
	 * This method provides a central point for loading class resource bundles.
	 * It provides a hook for changing the default load behavior of bundles.
	 *
	 * @param baseName Base name of bundle (typically fully qualified class name
	 *                 ).
	 * @param locale   Locale to get the bundle for.
	 * @param loader   Class loader from which to load the resource bundle
	 *                 ({@code null} to use system class loader).
	 *
	 * @return ResourceBundle for specified source and locale.
	 *
	 * @throws MissingResourceException if no resource bundle for the specified
	 * class can be found.
	 */
	@NotNull
	public static ResourceBundle getBundle( @NotNull final String baseName, @Nullable final Locale locale, @Nullable final ClassLoader loader )
	{
		final Locale usedLocale = ( locale == null ) ? Locale.getDefault() : locale;

		/*
		 * Use resource bundle from pre-loaded data if possible.
		 */
		ResourceBundle result = null;
		if ( _resourceData != null )
		{
			try
			{
				result = _resourceData.getBundle( baseName, usedLocale );
				if ( LOG.isDebugEnabled() )
				{
					LOG.debug( "Retrieved bundle from resource data: " + baseName + ", " + usedLocale );
				}
			}
			catch ( final MissingResourceException e )
			{
				// Fall back to regular resource bundle loading.
			}
		}

		/*
		 * Use regular resource bundle loading (not pre-loaded).
		 */
		if ( result == null )
		{
			if ( loader == null ) /* indicates void, basic type, or bootstrap class loader */
			{
				if ( LOG.isDebugEnabled() )
				{
					LOG.debug( "getBundle( '" + baseName + ", " + usedLocale + ", " + CONTROL + " )" );
				}
				result = ResourceBundle.getBundle( baseName, usedLocale, CONTROL ); /* uses system class loader, which extends the bootstrap class loader... */
			}
			else
			{
				if ( LOG.isDebugEnabled() )
				{
					LOG.debug( "getBundle( '" + baseName + ", " + usedLocale + ", " + loader + ", " + CONTROL + " )" );
				}
				result = ResourceBundle.getBundle( baseName, usedLocale, loader, CONTROL );
			}
		}

		return result;
	}

	/**
	 * Create {@link ResourceBundle} from the properties stored in the specified
	 * {@link Properties} object.
	 *
	 * @param properties {@link Properties} to create bundle from (may be {@code
	 *                   null}).
	 *
	 * @return {@link ResourceBundle} instance.
	 */
	@NotNull
	public static ResourceBundle getBundle( @Nullable final Properties properties )
	{
		final Map map;

		if ( properties != null )
		{
			map = new HashMap( properties.size() );
			for ( final Enumeration e = (Enumeration)properties.propertyNames(); e.hasMoreElements(); )
			{
				final String key = e.nextElement();
				final String value = properties.getProperty( key );

				map.put( key, value );
			}
		}
		else
		{
			map = new HashMap( 0 );
		}

		return new ResourceBundle()
		{
			@Override
			protected Object handleGetObject( @NotNull final String key )
			{
				return map.get( key );
			}

			@NotNull
			@Override
			public Enumeration getKeys()
			{
				return new CombinedBundleEnumeration( parent, map.keySet() );
			}
		};
	}

	/**
	 * Create {@link Properties} object from the string resources stored in the
	 * specified {@link ResourceBundle}.
	 *
	 * @param bundle {@link ResourceBundle} to get properties from (may be
	 *               {@code null}).
	 *
	 * @return {@link ResourceBundle} instance.
	 */
	@NotNull
	public static Properties getProperties( @Nullable final ResourceBundle bundle )
	{
		final Properties result = new Properties();

		if ( bundle != null )
		{
			for ( final Enumeration e = bundle.getKeys(); e.hasMoreElements(); )
			{
				final String key = e.nextElement();
				final Object value = bundle.getObject( key );

				if ( value instanceof String )
				{
					result.setProperty( key, (String)value );
				}
			}
		}

		return result;
	}

	/**
	 * Get list of strings from resource bundle with the specified key. Elements
	 * are separated by pipes (|). Leading and trailing whitespace of each
	 * element is removed.
	 *
	 * @param bundle Bundle to get the list from.
	 * @param key    Resource key of string list.
	 *
	 * @return List of strings from resource bundle.
	 *
	 * @throws MissingResourceException if no object for the given key can be
	 * found
	 */
	@NotNull
	public static String[] getStringList( @NotNull final ResourceBundle bundle, @NotNull final String key )
	{
		final String[] result = TextTools.tokenize( bundle.getString( key ), '|' );

		for ( int i = result.length; --i >= 0; )
		{
			result[ i ] = result[ i ].trim();
		}

		return result;
	}

	/**
	 * Get property choices from resource bundle for the specified property
	 * name. To make this work, the following resources must be defined:
	 *
	 * 
    * *
  • '{propertyName}List' is a list of possible values
  • * *
  • Optionally, a label for each value in the list
  • * *
* * @param bundle Bundle to get the list from. * @param propertyName Name of property for which to get the choices. * * @return Array with value/label pairs for all available choices. */ @NotNull public static String[] getChoices( @NotNull final ResourceBundle bundle, @Nullable final String propertyName ) { return getChoices( bundle, propertyName + '.', getStringList( bundle, propertyName + "List" ) ); } /** * Get property choices from resource bundle for the specified property name * based on a list of allowed property values. The choice labels are * determined for each value in the following order: * *
    * *
  1. Resource entry with the prefix concatenated with value as key; * *
  2. Resource entry with the value as key; * *
  3. The value itself. * *
* * @param bundle Bundle to get value labels from. * @param prefix Prefix to keys in bundle (optional). * @param values List of values to include in choices. * * @return Array with value/label pairs for all available choices. */ @NotNull public static String[] getChoices( @Nullable final ResourceBundle bundle, @Nullable final String prefix, @NotNull final String[] values ) { final String[] result = new String[ values.length * 2 ]; int resultIndex = 0; for ( final String value : values ) { String label = null; if ( ( prefix != null ) && !prefix.isEmpty() ) { label = getString( bundle, prefix + value, null ); } if ( label == null ) { label = getString( bundle, value, value ); } result[ resultIndex++ ] = value; result[ resultIndex++ ] = label; } return result; } /** * Get integer from resource bundle. If the requested integer is not or * incorrectly defined the bundle (or if {@code null} is specified for the * bundle or key), the specified default value is returned. * * @param bundle Bundle to get integer from. * @param key Key of integer to get from bundle. * @param defaultValue Value to return when the resource is not found. * * @return Integer from resource bundle; default value if undefined or * invalid. */ public static int getInt( @Nullable final ResourceBundle bundle, @NotNull final String key, final int defaultValue ) { int result = defaultValue; final String string = getString( bundle, key, null ); if ( string != null ) { try { result = Integer.parseInt( string ); } catch ( final NumberFormatException e ) { /* ignored, will return default value */ } } return result; } /** * Get string from resource for an enumerate type. * * @param locale Locale to get resource string for. * @param enumerate Enumerate value. * * @return String from resource bundle, or default value if undefined. */ @NotNull public static String getString( @Nullable final Locale locale, @NotNull final Enum enumerate ) { String result; try { result = getString( getBundleHierarchy( enumerate.getClass(), locale ), enumerate ); } catch ( final MissingResourceException ignored ) { result = enumerate.name(); } return result; } /** * Get string from resource for an enumerate type. * * @param bundle Bundle to get string from. * @param enumerate Enumerate value. * * @return String from resource bundle, or default value if undefined. */ @NotNull public static String getString( @Nullable final ResourceBundle bundle, @NotNull final Enum enumerate ) { String result; final String name = enumerate.name(); if ( bundle != null ) { try { final Class enumClass = enumerate.getClass(); String enumName = enumClass.getSimpleName(); enumName = enumName.substring( enumName.lastIndexOf( (int)'$' ) + 1 ); result = bundle.getString( enumName + '.' + name ); } catch ( final MissingResourceException ignored ) { result = getString( bundle, name, name ); } } else { result = name; } return result; } /** * Get string from resource bundle for the specified class and locale. If no * locale is specified, the default locale is used. * * @param locale Locale to get the bundle for. * @param clazz Class to get the bundle for. * @param key Key of string to get from bundle. * * @return String from resource bundle. * * @throws MissingResourceException if no resource bundle or resource entry * for the specified class can be found. */ @NotNull public static String getString( @Nullable final Locale locale, @NotNull final Class clazz, @NotNull final String key ) { final ResourceBundle bundle = getBundleHierarchy( clazz, locale ); try { return bundle.getString( key ); } catch ( final MissingResourceException ignored ) { throw new MissingResourceException( "Resource '" + key + "' not found in '" + clazz.getName() + "' bundle for locale " + locale, bundle.getClass().getName(), key ); } } /** * Get string from resource bundle. If the requested string is not defined * in the bundle (or if {@code null} is specified for the bundle or key), * the specified default value is returned. * * @param bundle Bundle to get string from. * @param key Key of string to get from bundle. * @param defaultValue Value to return when the string is not found. * * @return String from resource bundle, or default value if undefined. */ @Contract( "_, _, !null -> !null" ) @Nullable public static String getString( @Nullable final ResourceBundle bundle, @NotNull final String key, @Nullable final String defaultValue ) { String result = defaultValue; if ( bundle != null ) { try { result = bundle.getString( key ); } catch ( final MissingResourceException e ) { /* ignored, will return default value */ } } return result; } /** * Get message pattern with the specified key from the specified bundle and * format the message by substituting the specified argument. * * @param bundle Bundle to get message from. * @param key Key of message pattern to get from bundle. * @param argument Argument {@code {0}} to format and substitute. * * @return Formatted message. * * @throws IllegalArgumentException if the message pattern or arguments are * invalid. * @see MessageFormat#format(String, Object[]) */ @NotNull public static String format( @Nullable final ResourceBundle bundle, @NotNull final String key, final long argument ) { return MessageFormat.format( getString( bundle, key, key ), argument ); } /** * Get message pattern with the specified key from the specified bundle and * format the message by substituting the specified argument. * * @param bundle Bundle to get message from. * @param key Key of message pattern to get from bundle. * @param argument Argument {@code {0}} to format and substitute. * * @return Formatted message. * * @throws IllegalArgumentException if the message pattern or arguments are * invalid. * @see MessageFormat#format(String, Object[]) */ @NotNull public static String format( @Nullable final ResourceBundle bundle, @NotNull final String key, final double argument ) { return MessageFormat.format( getString( bundle, key, key ), argument ); } /** * Get message pattern with the specified key from the specified bundle and * format the message by substituting the specified arguments. * * @param bundle Bundle to get message from. * @param key Key of message pattern to get from bundle. * @param arguments Array of objects to be formatted and substituted. * * @return Formatted message. * * @throws IllegalArgumentException if the message pattern or arguments are * invalid. * @see MessageFormat#format(String, Object[]) */ @NotNull public static String format( @Nullable final ResourceBundle bundle, @NotNull final String key, final Object... arguments ) { return MessageFormat.format( getString( bundle, key, key ), arguments ); } /** * Get message pattern with the specified key from the specified bundle and * format the message by substituting the specified arguments. * * @param locale Locale used to format the pattern. * @param bundle Bundle to get message from. * @param key Key of message pattern to get from bundle. * @param arguments Array of objects to be formatted and substituted. * * @return Formatted message. * * @throws IllegalArgumentException if the message pattern or arguments are * invalid. * @see MessageFormat#format(String, Object[]) */ @NotNull public static String format( @Nullable final Locale locale, @Nullable final ResourceBundle bundle, @NotNull final String key, final Object... arguments ) { return new MessageFormat( getString( bundle, key, key ), locale ).format( arguments ); } /** * Get message pattern with the specified key from the specified bundle and * format the message by substituting the specified arguments. * * @param locale Locale used to format the pattern. * @param clazz Class from whose bundle to get message from. * @param key Key of message pattern to get from bundle. * @param arguments Array of objects to be formatted and substituted. * * @return Formatted message. * * @throws IllegalArgumentException if the message pattern or arguments are * invalid. * @see MessageFormat#format(String, Object[]) */ @NotNull public static String format( @Nullable final Locale locale, @NotNull final Class clazz, @NotNull final String key, final Object... arguments ) { return format( locale, getBundleHierarchy( clazz, locale ), key, arguments ); } /** * {@link ControlWithoutFallback} used by {@link #getBundle(Class, * Locale)}. */ private static final ResourceBundle.Control CONTROL = new ControlWithoutFallback(); /** * Implementation of {@link ResourceBundle.Control} that does not fallback * to the default locale. */ private static class ControlWithoutFallback extends ResourceBundle.Control { @Nullable @Override public Locale getFallbackLocale( @Nullable final String baseName, @Nullable final Locale locale ) { return null; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy