// Copyright (c) 2004-2005 Sun Microsystems Inc., All Rights Reserved.
/*
 * StringTranslator.java
 *
 * SUN PROPRIETARY/CONFIDENTIAL.
 * This software is the proprietary information of Sun Microsystems, Inc.  
 * Use is subject to license terms.
 * 
 */
package com.sun.enterprise.jbi.serviceengine.util.soap;

import java.text.MessageFormat;

import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Logger;


/**
 * This is the implementation of the String Translator, which provides services
 * for internationalization of messages to all services running inside the JBI
 * environment.
 *
 * @author Sun Microsystems Inc.
 */
public class StringTranslator
{
    /**
     * Logger name
     */
    private static final String LOGGER_NAME = "com.sun.jbi.common.soap";

    /**
     * Unqualified name for resource bundles.
     */
    public static final String RESOURCE_BUNDLE_NAME = "LocalStrings";

    /**
     * Log message for creation of new instance.
     */
    private static final String LOG_NEW_INSTANCE =
        "New StringTranslator for package {0}, classLoader is {1}";

    /**
     * Log message for locale.
     */
    private static final String LOG_CURRENT_LOCALE = "Current locale is {0}";

    /**
     * Log message for failure loading resource bundle.
     */
    private static final String LOG_UNABLE_TO_LOAD_BUNDLE =
        "Unable to load resource bundle {0} for locale {1}: {2}";

    /**
     * Log message for using alternate resource bundle.
     */
    private static final String LOG_USING_BUNDLE =
        "Using resource bundle for locale {0} instead.";

    /**
     * Log message for using fallback resource bundle to look up a message.
     */
    private static final String LOG_TRANSLATION_USING_FALLBACK =
        "No translation for key={0} found in resource bundle for locale {1}, "
        + "using locale {2} instead.";

    /**
     * Log message for no translation available for a message key in any
     * resource bundle.
     */
    private static final String LOG_NO_TRANSLATION_FOR_KEY =
        "No translation for key={0} found in any resource bundle. "
        + "Insert data is [{1}].";

    /**
     * Log message for no translation available for a message key in a
     * particular resource bundle.
     */
    private static final String LOG_NO_TRANSLATION_FOR_KEY_IN_BUNDLE =
        "No translation for key={0} found in resource bundle for locale {1}. "
        + "Insert data is [{2}].";

    /**
     * Message text used when no translation is available for a message key.
     */
    private static final String MSG_NO_TRANSLATION =
        "No translation available for message with key={0} and inserts=[{1}].";

    /**
     * The default locale at the time this StringTranslator was created.
     */
    private Locale mDefaultLocale;

    /**
     * Logger for this instance
     */
    private Logger mLog;

    /**
     * Fallback ResourceBundle for a single package name. This is used only
     * when the default locale is something other than Locale.US. In that
     * case, this is the bundle for Locale.US for looking up messages that are
     * not found in the default locale.
     */
    private ResourceBundle mFallbackBundle;

    /**
     * ResourceBundle for a single package name.
     */
    private ResourceBundle mResourceBundle;

    /**
     * Constructor. This loads the Resource Bundle for the current locale, and
     * if the current locale is not Locale.US, it loads the Resource Bundle
     * for Locale.US and stores it as the backup for string lookup.
     *
     * @param packageName - the name of the package that contains the resource
     *        bundle.
     * @param classLoader - the class loader to be used for loading the
     *        resource bundle. If this parameter is null, the current class
     *        loader is used.
     */
    public StringTranslator(
        String packageName,
        ClassLoader classLoader)
    {
        mLog = Logger.getLogger(packageName);

        String bundleName = packageName + "." + RESOURCE_BUNDLE_NAME;
        mDefaultLocale = Locale.getDefault();

        // Always load the bundle for english
        mFallbackBundle = null;

        ResourceBundle englishBundle = null;

        try
        {
            if (null == classLoader)
            {
                englishBundle = ResourceBundle.getBundle(bundleName, Locale.US);
            }
            else
            {
                englishBundle =
                    ResourceBundle.getBundle(bundleName, Locale.US, classLoader);
            }
        }
        catch (java.util.MissingResourceException mrEx)
        {
            mLog.warning(MessageFormat.format(LOG_UNABLE_TO_LOAD_BUNDLE,
                    new Object [] {bundleName, Locale.US, mrEx}));
        }

        // If the default locale is English, set it as the primary bundle.
        // If the default locale is not English, attempt to load the bundle.
        // If it is found, save it as the primary and set the fallback to
        // English. If it is not found, set the primary to English.
        if (mDefaultLocale.equals(Locale.US))
        {
            mResourceBundle = englishBundle;
        }
        else
        {
            try
            {
                if (null == classLoader)
                {
                    mResourceBundle = ResourceBundle.getBundle(bundleName);
                    mFallbackBundle = englishBundle;
                }
                else
                {
                    mResourceBundle =
                        ResourceBundle.getBundle(bundleName, mDefaultLocale,
                            classLoader);
                    mFallbackBundle = englishBundle;
                }
            }
            catch (java.util.MissingResourceException mrEx)
            {
                mLog.warning(MessageFormat.format(LOG_UNABLE_TO_LOAD_BUNDLE,
                        new Object [] {bundleName, mDefaultLocale, mrEx}));
                mLog.warning(MessageFormat.format(LOG_USING_BUNDLE,
                        new Object [] {Locale.US}));
                mResourceBundle = englishBundle;
            }
        }
    }

    /**
     * Get a localized string using the specified resource key.
     *
     * @param key - the key to the localized string in the resource bundle.
     *
     * @return the localized string.
     */
    public String getString(String key)
    {
        Object [] inserts = new Object[0];

        return getString(key, inserts);
    }

    /**
     * Get a localized string using the specified resource key. Handle one
     * message insert.
     *
     * @param key - the key to the localized string in the resource bundle.
     * @param insert1 - the message insert.
     *
     * @return the localized string formatted with the message insert.
     */
    public String getString(
        String key,
        Object insert1)
    {
        Object [] inserts = {insert1};

        return getString(key, inserts);
    }

    /**
     * Get a localized string using the specified resource key. Handle two
     * message inserts.
     *
     * @param key - the key to the localized string in the resource bundle.
     * @param insert1 - the first message insert.
     * @param insert2 - the second message insert.
     *
     * @return the localized string formatted with the message inserts.
     */
    public String getString(
        String key,
        Object insert1,
        Object insert2)
    {
        Object [] inserts = {insert1, insert2};

        return getString(key, inserts);
    }

    /**
     * Get a localized string using the specified resource key. Handle three
     * message inserts.
     *
     * @param key - the key to the localized string in the resource bundle.
     * @param insert1 - the first message insert.
     * @param insert2 - the second message insert.
     * @param insert3 - the third message insert.
     *
     * @return the localized string formatted with the message inserts.
     */
    public String getString(
        String key,
        Object insert1,
        Object insert2,
        Object insert3)
    {
        Object [] inserts = {insert1, insert2, insert3};

        return getString(key, inserts);
    }

    /**
     * Get a localized string using the specified resource key. Handle four
     * message inserts.
     *
     * @param key - the key to the localized string in the resource bundle.
     * @param insert1 - the first message insert.
     * @param insert2 - the second message insert.
     * @param insert3 - the third message insert.
     * @param insert4 - the fourth message insert.
     *
     * @return the localized string formatted with the message inserts.
     */
    public String getString(
        String key,
        Object insert1,
        Object insert2,
        Object insert3,
        Object insert4)
    {
        Object [] inserts = {insert1, insert2, insert3, insert4};

        return getString(key, inserts);
    }

    /**
     * Get a localized string using the specified resource key. Handle any
     * number of message inserts. This method is called by all the other
     * getString() methods in the class. The procedure for string lookup is to
     * first look in the primary resource bundle, then in the fallback
     * resource bundle. If the string is found in the primary, return the
     * translated string quietly. If the string is not found in the primary
     * but in the fallback, log a warning and return the translated string. If
     * the string is not found in either bundle, log an error and return a
     * message formatted with the key and insert values provided by the
     * caller. If there is no resource bundle available, just return a message
     * formatted with the key and insert values provided by the caller.
     *
     * @param key - the key to the localized string in the resource bundle.
     * @param inserts - the array of message inserts.
     *
     * @return the localized string formatted with the message inserts.
     */
    public String getString(
        String key,
        Object [] inserts)
    {
        String translated = null;

        if (null != mResourceBundle)
        {
            try
            {
                translated = mResourceBundle.getString(key);
                translated = MessageFormat.format(translated, inserts);
            }
            catch (java.util.MissingResourceException mrEx)
            {
                if (null != mFallbackBundle)
                {
                    try
                    {
                        translated = mFallbackBundle.getString(key);
                        translated = MessageFormat.format(translated, inserts);
                        mLog.info(MessageFormat.format(
                                LOG_TRANSLATION_USING_FALLBACK,
                                new Object [] {key, mDefaultLocale, Locale.US}));
                    }
                    catch (java.util.MissingResourceException mreEx)
                    {
                        String fi = formatInserts(inserts);
                        translated =
                            MessageFormat.format(MSG_NO_TRANSLATION,
                                new Object [] {key, fi});
                        mLog.warning(MessageFormat.format(
                                LOG_NO_TRANSLATION_FOR_KEY,
                                new Object [] {key, fi}));
                    }
                }
                else
                {
                    String fi = formatInserts(inserts);
                    translated =
                        MessageFormat.format(MSG_NO_TRANSLATION,
                            new Object [] {key, fi});
                    mLog.warning(MessageFormat.format(
                            LOG_NO_TRANSLATION_FOR_KEY_IN_BUNDLE,
                            new Object [] {key, mDefaultLocale, fi}));
                }
            }
        }
        else
        {
            translated =
                MessageFormat.format(MSG_NO_TRANSLATION,
                    new Object [] {key, formatInserts(inserts)});
        }

        return translated;
    }

    /**
     * Format an array of message inserts into a string. The ouptut string is
     * in the format "insert1,insert2,....,insertn".
     *
     * @param inserts - the array of message inserts.
     *
     * @return the string formatted with the message inserts.
     */
    private String formatInserts(Object [] inserts)
    {
        StringBuffer formatted = new StringBuffer("");

        for (int i = 0; i < inserts.length; i++)
        {
            if (i > 0)
            {
                formatted.append(",");
            }

            formatted.append(inserts[i].toString());
        }

        return formatted.toString();
    }
}
