code: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you 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.
   */
  /*
   * $Id$
   */
  package org.apache.xml.serializer.utils;
  
  import java.util.ListResourceBundle;
  import java.util.Locale;
  import java.util.MissingResourceException;
  import java.util.ResourceBundle;
  
  /**
   * A utility class for issuing error messages.
   * 
   * A user of this class normally would create a singleton 
   * instance of this class, passing the name
   * of the message class on the constructor. For example:
   * <CODE>
   * static Messages x = new Messages("org.package.MyMessages");
   * </CODE>
   * Later the message is typically generated this way if there are no 
   * substitution arguments:
   * <CODE>
   * String msg = x.createMessage(org.package.MyMessages.KEY_ONE, null); 
   * </CODE>
   * If there are arguments substitutions then something like this:
   * <CODE>
   * String filename = ...;
   * String directory = ...;
   * String msg = x.createMessage(org.package.MyMessages.KEY_TWO, 
   *   new Object[] {filename, directory) ); 
   * </CODE>
   *  
   * The constructor of an instance of this class must be given
   * the class name of a class that extends java.util.ListResourceBundle 
   * ("org.package.MyMessages" in the example above).  
   * The name should not have any language suffix 
   * which will be added automatically by this utility class.
   * 
   * The message class ("org.package.MyMessages")
   * must define the abstract method getContents() that is
   * declared in its base class, for example:
   * <CODE>
   * public Object[][] getContents() {return contents;}
   * </CODE>
   * 
   * It is suggested that the message class expose its
   * message keys like this:
   * <CODE>
   *   public static final String KEY_ONE = "KEY1";
   *   public static final String KEY_TWO = "KEY2";
   *   . . . 
   * </CODE>
   * and used through their names (KEY_ONE ...) rather than
   * their values ("KEY1" ...).
   * 
   * The field contents (returned by getContents()
   * should be initialized something like this:
   * <CODE>
   * public static final Object[][] contents = {
   * { KEY_ONE, "Something has gone wrong!" },
   * { KEY_TWO, "The file ''{0}'' does not exist in directory ''{1}''." },
   * . . .
   * { KEY_N, "Message N" }  }
   * </CODE>
   * 
   * Where that section of code with the KEY to Message mappings
   * (where the message classes 'contents' field is initialized)
   * can have the Message strings translated in an alternate language
   * in a errorResourceClass with a language suffix.
   * 
   * More sophisticated use of this class would be to pass null
   * when contructing it, but then call loadResourceBundle()
   * before creating any messages.
   * 
   * This class is not a public API, it is only public because it is 
   * used in org.apache.xml.serializer.
   *
   *  @xsl.usage internal
   */
  public final class Messages
  {
      /** The local object to use.  */
      private final Locale m_locale = Locale.getDefault();
  
      /** The language specific resource object for messages.  */
      private ListResourceBundle m_resourceBundle;
  
      /** The class name of the error message string table with no language suffix. */
      private String m_resourceBundleName;
  
  
  
      /**
       * Constructor.
       * @param resourceBundle the class name of the ListResourceBundle
       * that the instance of this class is associated with and will use when
       * creating messages.
       * The class name is without a language suffix. If the value passed
       * is null then loadResourceBundle(errorResourceClass) needs to be called
       * explicitly before any messages are created.
       * 
       * @xsl.usage internal
       */
      Messages(String resourceBundle)
      {
  
          m_resourceBundleName = resourceBundle;
      }
      
      /*
       * Set the Locale object to use. If this method is not called the
       * default locale is used. This method needs to be called before
       * loadResourceBundle().
       * 
       * @param locale non-null reference to Locale object.
       * @xsl.usage internal
       */
  //    public void setLocale(Locale locale)
  //    {
  //        m_locale = locale;
  //    }
  
      /**
       * Get the Locale object that is being used.
       * 
       * @return non-null reference to Locale object.
       * @xsl.usage internal
       */
      private Locale getLocale()
      {
          return m_locale;
      }
  
      /**
       * Get the ListResourceBundle being used by this Messages instance which was
       * previously set by a call to loadResourceBundle(className)
       * @xsl.usage internal
       */
      private ListResourceBundle getResourceBundle()
      {
          return m_resourceBundle;
      }
  
      /**
       * Creates a message from the specified key and replacement
       * arguments, localized to the given locale.
       *
       * @param msgKey  The key for the message text.
       * @param args    The arguments to be used as replacement text
       * in the message created.
       *
       * @return The formatted message string.
       * @xsl.usage internal
       */
      public final String createMessage(String msgKey, Object args[])
      {
          if (m_resourceBundle == null)
              m_resourceBundle = loadResourceBundle(m_resourceBundleName);
  
          if (m_resourceBundle != null)
          {
              return createMsg(m_resourceBundle, msgKey, args);
          }
          else
              return "Could not load the resource bundles: "+ m_resourceBundleName;
      }
  
      /**
       * Creates a message from the specified key and replacement
       * arguments, localized to the given locale.
       *
       * @param errorCode The key for the message text.
       *
       * @param fResourceBundle The resource bundle to use.
       * @param msgKey  The message key to use.
       * @param args      The arguments to be used as replacement text
       *                  in the message created.
       *
       * @return The formatted message string.
       * @xsl.usage internal
       */
      private final String createMsg(
          ListResourceBundle fResourceBundle,
          String msgKey,
          Object args[]) //throws Exception
      {
  
          String fmsg = null;
          boolean throwex = false;
          String msg = null;
  
          if (msgKey != null)
              msg = fResourceBundle.getString(msgKey);
          else
              msgKey = "";
  
          if (msg == null)
          {
              throwex = true;
              /* The message is not in the bundle . . . this is bad,
               * so try to get the message that the message is not in the bundle
               */
              try
              {
  
                  msg =
                      java.text.MessageFormat.format(
                          MsgKey.BAD_MSGKEY,
                          new Object[] { msgKey, m_resourceBundleName });
              }
              catch (Exception e)
              {
                  /* even the message that the message is not in the bundle is
                   * not there ... this is really bad
                   */
                  msg =
                      "The message key '"
                          + msgKey
                          + "' is not in the message class '"
                          + m_resourceBundleName+"'";
              }
          }
          else if (args != null)
          {
              try
              {
                  // Do this to keep format from crying.
                  // This is better than making a bunch of conditional
                  // code all over the place.
                  int n = args.length;
  
                  for (int i = 0; i < n; i++)
                  {
                      if (null == args[i])
                          args[i] = "";
                  }
  
                  fmsg = java.text.MessageFormat.format(msg, args);
                  // if we get past the line above we have create the message ... hurray!
              }
              catch (Exception e)
              {
                  throwex = true;
                  try
                  {
                      // Get the message that the format failed.
                      fmsg =
                          java.text.MessageFormat.format(
                              MsgKey.BAD_MSGFORMAT,
                              new Object[] { msgKey, m_resourceBundleName });
                      fmsg += " " + msg;
                  }
                  catch (Exception formatfailed)
                  {
                      // We couldn't even get the message that the format of
                      // the message failed ... so fall back to English.
                      fmsg =
                          "The format of message '"
                              + msgKey
                              + "' in message class '"
                              + m_resourceBundleName
                              + "' failed.";
                  }
              }
          }
          else
              fmsg = msg;
  
          if (throwex)
          {
              throw new RuntimeException(fmsg);
          }
  
          return fmsg;
      }
  
      /**
       * Return a named ResourceBundle for a particular locale.  This method mimics the behavior
       * of ResourceBundle.getBundle().
       * 
       * @param className the name of the class that implements ListResourceBundle,
       * without language suffix.
       * @return the ResourceBundle
       * @throws MissingResourceException
       * @xsl.usage internal
       */
      private ListResourceBundle loadResourceBundle(String resourceBundle)
          throws MissingResourceException
      {
          m_resourceBundleName = resourceBundle;
          Locale locale = getLocale();
  
          ListResourceBundle lrb;
  
          try
          {
  
              ResourceBundle rb =
                  ResourceBundle.getBundle(m_resourceBundleName, locale);
              lrb = (ListResourceBundle) rb;
          }
          catch (MissingResourceException e)
          {
              try // try to fall back to en_US if we can't load
                  {
  
                  // Since we can't find the localized property file,
                  // fall back to en_US.
                  lrb =
                      (ListResourceBundle) ResourceBundle.getBundle(
                          m_resourceBundleName,
                          new Locale("en", "US"));
              }
              catch (MissingResourceException e2)
              {
  
                  // Now we are really in trouble.
                  // very bad, definitely very bad...not going to get very far
                  throw new MissingResourceException(
                      "Could not load any resource bundles." + m_resourceBundleName,
                      m_resourceBundleName,
                      "");
              }
          }
          m_resourceBundle = lrb;
          return lrb;
      }
  
      /**
       * Return the resource file suffic for the indicated locale
       * For most locales, this will be based the language code.  However
       * for Chinese, we do distinguish between Taiwan and PRC
       *
       * @param locale the locale
       * @return an String suffix which can be appended to a resource name
       * @xsl.usage internal
       */
      private static String getResourceSuffix(Locale locale)
      {
  
          String suffix = "_" + locale.getLanguage();
          String country = locale.getCountry();
  
          if (country.equals("TW"))
              suffix += "_" + country;
  
          return suffix;
      }
  }
  