/**
* Copyright (c) 2009--2014 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package com.redhat.rhn.common.localization;
import com.redhat.rhn.common.conf.Config;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
/**
* Utility (Singleton) class to get and format messages centralized by package.
* Messages are assumed to be in a {@link ResourceBundle} called
* <code>StringResource</code>
* in a given class' package.
* ie, If the class is <code>my.package.foo.Bar</code>, the
* methods will look for the ResourceBundle
* <code>my.package.foo.StringResource</code>. (The actual file can be
* a subclass of ResourceBundle or a properties file.)
* The ResourceBundle keys are assumed to be prefixed with the class name.
* ie, if the class is <code>my.package.foo.Bar</code> and the key is
* <code>msg</code>, the methods will then
* look for the key <code>Bar.msg</code>.
*
*
* @version $Rev$
*/
public final class XmlMessages {
/** The error warning message in case we don't find a message.
*/
public static final String MESSAGE_NOT_FOUND = "*** MESSAGE NOT FOUND ***";
/** Set to "StringResource"
*/
private static final String RESOURCE_BUNDLE_CLASSNAME = "StringResource";
public static final String PRODUCT_NAME_MACRO = "@@PRODUCT_NAME@@";
private static final Logger LOG = Logger.getLogger(XmlMessages.class);
private static XmlMessages instance = new XmlMessages();
// Store the bundles in memory so we don't load it off disk
// each time.
private Map<String, ResourceBundle> bundles;
/** Private constructor, since this is a singleton
*/
private XmlMessages() {
initBundleCache();
}
private void initBundleCache() {
bundles = new HashMap<String, ResourceBundle>();
}
/**
* Get the instance of the singleton class
* @return The instance
*/
public static XmlMessages getInstance() {
return instance;
}
/**
* Reload the XML Messages off the disk.
*/
public void resetBundleCache() {
initBundleCache();
}
/** Gets the resource bundle, first checking our internal cache
* @param bundleName name of the resource bundle
* @param locale locale used to retrieve the resource bundle
* @return the resource bundle for the given bundleName and locale,
* always non-null. (An exception is thrown if the bundle can't be found.)
*/
protected ResourceBundle getBundle(final String bundleName,
final Locale locale) {
// Construct the key to the Map of Bundles
// that is a combination of the bundlename
// plus the locale
StringBuilder bundleBuff = new StringBuilder(bundleName);
if (locale != null) {
bundleBuff.append(".");
bundleBuff.append(locale.toString());
}
String bundleKey = bundleBuff.toString();
// Check the local in memory cache of the bundles
// to see if it has been loaded already.
ResourceBundle retval = bundles.get(bundleKey);
if (retval != null) {
// System.out.println("Got bundle from cache, returning : " + bundleKey);
return retval;
}
// System.out.println("Reloading BUNDLE : " + bundleKey);
StringBuilder urlName = new StringBuilder("/" + bundleName.replace('.', '/'));
// if we specified a locale
// then make sure we tack it on to the filename
// to be loaded.
if (locale != null) {
urlName.append("_");
urlName.append(locale.toString());
}
urlName.append(".xml");
try {
synchronized (this) {
retval =
new XmlResourceBundle(urlName.toString());
bundles.put(bundleKey, retval);
}
}
catch (IOException ioe) {
if (LOG.isDebugEnabled()) {
LOG.debug("Resource bundle not found: " +
ioe.toString() + ", url: " + urlName);
}
throw new java.util.MissingResourceException(
"Resource bundle not found", bundleName, "");
}
return retval;
}
/**
* Obtain a string from the resource file that doesn't require formatting.
* See the format() methods for obtaining strings that require formatting.
* @param clazz the class to which the string belongs
* @param locale the locale used to find the resource bundle
* @param key the key for the string to be obtained from the resource bundle
* @return the message for the given key
*/
public String getMessage(final Class clazz,
final Locale locale,
final String key) {
return format(clazz, locale, key, (Object[]) null);
}
/**
* Convenience method to format a string from the resource bundle, which
* takes a single argument.
* @param clazz the class to which the string belongs
* @param locale the locale used to find the resource bundle
* @param key the key for the string to be obtained from the resource bundle
* @param arg1 the first argument to use in the formatted text
* @return the formatted message for the given key and argument
*/
public String format(final Class clazz,
final Locale locale,
final String key,
final String arg1) {
return format(clazz, locale, key, new Object[]{arg1});
}
/**
* Convenience method to format a string from the resource bundle, which
* takes two arguments.
* @param clazz the class to which the string belongs
* @param locale the locale used to find the resource bundle
* @param key the key for the string to be obtained from the resource bundle
* @param arg1 the first argument to use in the formatted text
* @param arg2 the second argument to use in the formatted text
* @return the formatted message for the given key and arguments
*/
public String format(final Class clazz,
final Locale locale,
final String key,
final String arg1,
final String arg2) {
return format(clazz, locale, key, new Object[]{arg1, arg2});
}
/**
* Convenience method to format a string from the resource bundle, which
* takes three arguments.
* @param clazz the class to which the string belongs
* @param locale the locale used to find the resource bundle
* @param key the key for the string to be obtained from the resource bundle
* @param arg1 the first argument to use in the formatted text
* @param arg2 the second argument to use in the formatted text
* @param arg3 the third argument to use in the formatted text
* @return the formatted message for the given key and arguments
*/
public String format(final Class clazz,
final Locale locale,
final String key,
final String arg1,
final String arg2,
final String arg3) {
return format(clazz, locale, key, new Object[]{arg1, arg2, arg3});
}
/**
* Method to format a string from the resource bundle. This
* method allows the user of this class to directly specify the
* bundle package name to be used. Allows us to have
* resource bundles in packages without classes. Eliminates
* the need for "Dummy" classes.
*
* @param clazz the class to which the string belongs
* @param locale the locale used to find the resource bundle
* @param key the key for the string to be obtained from the resource bundle
* @param args the arguments that should be applied to the string obtained
* from the resource bundle. Can be null, to represent no arguments
* @return the formatted message for the given key and arguments
*/
public String format(final Class clazz,
final Locale locale,
final String key,
final Object... args) {
// Fetch the bundle
ResourceBundle bundle = getBundle(getBundleName(clazz), locale);
String pattern = StringEscapeUtils.unescapeHtml(bundle.getString(key));
pattern = pattern.replaceAll(PRODUCT_NAME_MACRO,
Config.get().getString("web.product_name"));
if (args == null || args.length == 0) {
return pattern;
}
//MessageFormat uses single quotes to escape text. Therefore, we have to
//escape the single quote so that MessageFormat keeps the single quote and
//does replace all arguments after it.
String escapedPattern = pattern.replaceAll("'", "''");
MessageFormat mf = new MessageFormat(escapedPattern, locale);
return mf.format(args);
}
private String getBundleName(final Class clazz) {
String fullyQualifiedClassName = clazz.getName();
int idx = fullyQualifiedClassName.lastIndexOf('.');
String bundleName = fullyQualifiedClassName.substring(0, idx + 1) +
RESOURCE_BUNDLE_CLASSNAME;
return bundleName;
}
/**
* Gets the keys of all the strings in the specified class and locale's
* resource bundle.
*
* @param clazz Class of the resource bundle
* @param locale locale used to retrieve the resource bundle
* @return the Iterator of all the keys
*/
public Enumeration getKeys(final Class clazz,
final Locale locale) {
return getBundle(getBundleName(clazz), locale).getKeys();
}
}