/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

// 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;

    public StringTranslator() {
        this("com.sun.enterprise.jbi.serviceengine.core", null);
    }
    
    private static StringTranslator defaultInstance;
    
    public static StringTranslator getDefaultInstance() {
        if (defaultInstance == null) {
            defaultInstance = new StringTranslator();
        }
        return defaultInstance;
    }
    
    /**
     * 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.fine(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();
    }
}
