Home | History | Annotate | Download | only in utils
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: Messages.java 468654 2006-10-28 07:09:23Z minchau $
     20  */
     21 package org.apache.xml.serializer.utils;
     22 
     23 import java.util.ListResourceBundle;
     24 import java.util.Locale;
     25 import java.util.MissingResourceException;
     26 import java.util.ResourceBundle;
     27 
     28 /**
     29  * A utility class for issuing error messages.
     30  *
     31  * A user of this class normally would create a singleton
     32  * instance of this class, passing the name
     33  * of the message class on the constructor. For example:
     34  * <CODE>
     35  * static Messages x = new Messages("org.package.MyMessages");
     36  * </CODE>
     37  * Later the message is typically generated this way if there are no
     38  * substitution arguments:
     39  * <CODE>
     40  * String msg = x.createMessage(org.package.MyMessages.KEY_ONE, null);
     41  * </CODE>
     42  * If there are arguments substitutions then something like this:
     43  * <CODE>
     44  * String filename = ...;
     45  * String directory = ...;
     46  * String msg = x.createMessage(org.package.MyMessages.KEY_TWO,
     47  *   new Object[] {filename, directory) );
     48  * </CODE>
     49  *
     50  * The constructor of an instance of this class must be given
     51  * the class name of a class that extends java.util.ListResourceBundle
     52  * ("org.package.MyMessages" in the example above).
     53  * The name should not have any language suffix
     54  * which will be added automatically by this utility class.
     55  *
     56  * The message class ("org.package.MyMessages")
     57  * must define the abstract method getContents() that is
     58  * declared in its base class, for example:
     59  * <CODE>
     60  * public Object[][] getContents() {return contents;}
     61  * </CODE>
     62  *
     63  * It is suggested that the message class expose its
     64  * message keys like this:
     65  * <CODE>
     66  *   public static final String KEY_ONE = "KEY1";
     67  *   public static final String KEY_TWO = "KEY2";
     68  *   . . .
     69  * </CODE>
     70  * and used through their names (KEY_ONE ...) rather than
     71  * their values ("KEY1" ...).
     72  *
     73  * The field contents (returned by getContents()
     74  * should be initialized something like this:
     75  * <CODE>
     76  * public static final Object[][] contents = {
     77  * { KEY_ONE, "Something has gone wrong!" },
     78  * { KEY_TWO, "The file ''{0}'' does not exist in directory ''{1}''." },
     79  * . . .
     80  * { KEY_N, "Message N" }  }
     81  * </CODE>
     82  *
     83  * Where that section of code with the KEY to Message mappings
     84  * (where the message classes 'contents' field is initialized)
     85  * can have the Message strings translated in an alternate language
     86  * in a errorResourceClass with a language suffix.
     87  *
     88  * More sophisticated use of this class would be to pass null
     89  * when contructing it, but then call loadResourceBundle()
     90  * before creating any messages.
     91  *
     92  * This class is not a public API, it is only public because it is
     93  * used in org.apache.xml.serializer.
     94  *
     95  *  @xsl.usage internal
     96  */
     97 public final class Messages
     98 {
     99     /** The local object to use.  */
    100     private final Locale m_locale = Locale.getDefault();
    101 
    102     /** The language specific resource object for messages.  */
    103     private ListResourceBundle m_resourceBundle;
    104 
    105     /** The class name of the error message string table with no language suffix. */
    106     private String m_resourceBundleName;
    107 
    108 
    109 
    110     /**
    111      * Constructor.
    112      * @param resourceBundle the class name of the ListResourceBundle
    113      * that the instance of this class is associated with and will use when
    114      * creating messages.
    115      * The class name is without a language suffix. If the value passed
    116      * is null then loadResourceBundle(errorResourceClass) needs to be called
    117      * explicitly before any messages are created.
    118      *
    119      * @xsl.usage internal
    120      */
    121     Messages(String resourceBundle)
    122     {
    123 
    124         m_resourceBundleName = resourceBundle;
    125     }
    126 
    127     /*
    128      * Set the Locale object to use. If this method is not called the
    129      * default locale is used. This method needs to be called before
    130      * loadResourceBundle().
    131      *
    132      * @param locale non-null reference to Locale object.
    133      * @xsl.usage internal
    134      */
    135 //    public void setLocale(Locale locale)
    136 //    {
    137 //        m_locale = locale;
    138 //    }
    139 
    140     /**
    141      * Get the Locale object that is being used.
    142      *
    143      * @return non-null reference to Locale object.
    144      * @xsl.usage internal
    145      */
    146     private Locale getLocale()
    147     {
    148         return m_locale;
    149     }
    150 
    151     /**
    152      * Get the ListResourceBundle being used by this Messages instance which was
    153      * previously set by a call to loadResourceBundle(className)
    154      * @xsl.usage internal
    155      */
    156     private ListResourceBundle getResourceBundle()
    157     {
    158         return m_resourceBundle;
    159     }
    160 
    161     /**
    162      * Creates a message from the specified key and replacement
    163      * arguments, localized to the given locale.
    164      *
    165      * @param msgKey  The key for the message text.
    166      * @param args    The arguments to be used as replacement text
    167      * in the message created.
    168      *
    169      * @return The formatted message string.
    170      * @xsl.usage internal
    171      */
    172     public final String createMessage(String msgKey, Object args[])
    173     {
    174         if (m_resourceBundle == null)
    175             m_resourceBundle = loadResourceBundle(m_resourceBundleName);
    176 
    177         if (m_resourceBundle != null)
    178         {
    179             return createMsg(m_resourceBundle, msgKey, args);
    180         }
    181         else
    182             return "Could not load the resource bundles: "+ m_resourceBundleName;
    183     }
    184 
    185     /**
    186      * Creates a message from the specified key and replacement
    187      * arguments, localized to the given locale.
    188      *
    189      * @param errorCode The key for the message text.
    190      *
    191      * @param fResourceBundle The resource bundle to use.
    192      * @param msgKey  The message key to use.
    193      * @param args      The arguments to be used as replacement text
    194      *                  in the message created.
    195      *
    196      * @return The formatted message string.
    197      * @xsl.usage internal
    198      */
    199     private final String createMsg(
    200         ListResourceBundle fResourceBundle,
    201         String msgKey,
    202         Object args[]) //throws Exception
    203     {
    204 
    205         String fmsg = null;
    206         boolean throwex = false;
    207         String msg = null;
    208 
    209         if (msgKey != null)
    210             msg = fResourceBundle.getString(msgKey);
    211         else
    212             msgKey = "";
    213 
    214         if (msg == null)
    215         {
    216             throwex = true;
    217             /* The message is not in the bundle . . . this is bad,
    218              * so try to get the message that the message is not in the bundle
    219              */
    220             try
    221             {
    222 
    223                 msg =
    224                     java.text.MessageFormat.format(
    225                         MsgKey.BAD_MSGKEY,
    226                         new Object[] { msgKey, m_resourceBundleName });
    227             }
    228             catch (Exception e)
    229             {
    230                 /* even the message that the message is not in the bundle is
    231                  * not there ... this is really bad
    232                  */
    233                 msg =
    234                     "The message key '"
    235                         + msgKey
    236                         + "' is not in the message class '"
    237                         + m_resourceBundleName+"'";
    238             }
    239         }
    240         else if (args != null)
    241         {
    242             try
    243             {
    244                 // Do this to keep format from crying.
    245                 // This is better than making a bunch of conditional
    246                 // code all over the place.
    247                 int n = args.length;
    248 
    249                 for (int i = 0; i < n; i++)
    250                 {
    251                     if (null == args[i])
    252                         args[i] = "";
    253                 }
    254 
    255                 fmsg = java.text.MessageFormat.format(msg, args);
    256                 // if we get past the line above we have create the message ... hurray!
    257             }
    258             catch (Exception e)
    259             {
    260                 throwex = true;
    261                 try
    262                 {
    263                     // Get the message that the format failed.
    264                     fmsg =
    265                         java.text.MessageFormat.format(
    266                             MsgKey.BAD_MSGFORMAT,
    267                             new Object[] { msgKey, m_resourceBundleName });
    268                     fmsg += " " + msg;
    269                 }
    270                 catch (Exception formatfailed)
    271                 {
    272                     // We couldn't even get the message that the format of
    273                     // the message failed ... so fall back to English.
    274                     fmsg =
    275                         "The format of message '"
    276                             + msgKey
    277                             + "' in message class '"
    278                             + m_resourceBundleName
    279                             + "' failed.";
    280                 }
    281             }
    282         }
    283         else
    284             fmsg = msg;
    285 
    286         if (throwex)
    287         {
    288             throw new RuntimeException(fmsg);
    289         }
    290 
    291         return fmsg;
    292     }
    293 
    294     /**
    295      * Return a named ResourceBundle for a particular locale.  This method mimics the behavior
    296      * of ResourceBundle.getBundle().
    297      *
    298      * @param className the name of the class that implements ListResourceBundle,
    299      * without language suffix.
    300      * @return the ResourceBundle
    301      * @throws MissingResourceException
    302      * @xsl.usage internal
    303      */
    304     private ListResourceBundle loadResourceBundle(String resourceBundle)
    305         throws MissingResourceException
    306     {
    307         m_resourceBundleName = resourceBundle;
    308         Locale locale = getLocale();
    309 
    310         ListResourceBundle lrb;
    311 
    312         try
    313         {
    314 
    315             ResourceBundle rb =
    316                 ResourceBundle.getBundle(m_resourceBundleName, locale);
    317             lrb = (ListResourceBundle) rb;
    318         }
    319         catch (MissingResourceException e)
    320         {
    321             try // try to fall back to en_US if we can't load
    322                 {
    323 
    324                 // Since we can't find the localized property file,
    325                 // fall back to en_US.
    326                 lrb =
    327                     (ListResourceBundle) ResourceBundle.getBundle(
    328                         m_resourceBundleName,
    329                         new Locale("en", "US"));
    330             }
    331             catch (MissingResourceException e2)
    332             {
    333 
    334                 // Now we are really in trouble.
    335                 // very bad, definitely very bad...not going to get very far
    336                 throw new MissingResourceException(
    337                     "Could not load any resource bundles." + m_resourceBundleName,
    338                     m_resourceBundleName,
    339                     "");
    340             }
    341         }
    342         m_resourceBundle = lrb;
    343         return lrb;
    344     }
    345 
    346     /**
    347      * Return the resource file suffic for the indicated locale
    348      * For most locales, this will be based the language code.  However
    349      * for Chinese, we do distinguish between Taiwan and PRC
    350      *
    351      * @param locale the locale
    352      * @return an String suffix which can be appended to a resource name
    353      * @xsl.usage internal
    354      */
    355     private static String getResourceSuffix(Locale locale)
    356     {
    357 
    358         String suffix = "_" + locale.getLanguage();
    359         String country = locale.getCountry();
    360 
    361         if (country.equals("TW"))
    362             suffix += "_" + country;
    363 
    364         return suffix;
    365     }
    366 }
    367