View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package javax.jdo.spi;
    19
    20  import java.util.*;
    21  import java.text.MessageFormat;
    22  import java.security.AccessController;
    23  import java.security.PrivilegedAction;
    24
    25  import javax.jdo.JDOFatalInternalException;
    26
    27  /** Helper class for constructing messages from bundles.  The intended usage
    28   * of this class is to construct a new instance bound to a bundle, as in
    29   * <P>
    30   * <code>I18NHelper msg = I18NHelper.getInstance("javax.jdo.Bundle");</code>
    31   * <P>
    32   * This call uses the class loader that loaded the I18NHelper class to find
    33   * the specified Bundle. The class provides two overloaded getInstance
    34   * methods allowing to specify a different class loader:
    35   * {@link #getInstance(Class cls)} looks for a bundle
    36   * called "Bundle.properties" located in the package of the specified class
    37   * object and {@link #getInstance(String bundleName,ClassLoader loader)}
    38   * uses the specified class loader to find the bundle.
    39   * <P>
    40   * Subsequently, instance methods can be used to format message strings
    41   * using the text from the bundle, as in
    42   * <P>
    43   * <code>throw new JDOFatalInternalException (msg.msg("ERR_NoMetadata",
    44   * cls.getName()));</code>
    45   * @since 1.0.1
    46   * @version 1.1
    47   */
    48  public class I18NHelper {
    49
    50      /** Bundles that have already been loaded
    51       */
    52      private static Hashtable<String,ResourceBundle>
    53              bundles = new Hashtable<String,ResourceBundle>();
    54
    55      /** Helper instances that have already been created
    56       */
    57      private static Hashtable<String,I18NHelper>
    58              helpers = new Hashtable<String,I18NHelper>();
    59
    60      /** The default locale for this VM.
    61       */
    62      private static Locale       locale = Locale.getDefault();
    63
    64      /** The bundle used by this instance of the helper.
    65       */
    66      private ResourceBundle      bundle = null;
    67
    68      /** Throwable if ResourceBundle couldn't be loaded
    69       */
    70      private Throwable           failure = null;
    71
    72      /** The unqualified standard name of a bundle. */
    73      private static final String bundleSuffix = ".Bundle";    // NOI18N
    74
    75      /** Constructor */
    76      private I18NHelper() {
    77      }
    78
    79      /** Constructor for an instance bound to a bundle.
    80       * @param bundleName the name of the resource bundle
    81       * @param loader the class loader from which to load the resource
    82       * bundle
    83       */
    84      private I18NHelper (String bundleName, ClassLoader loader) {
    85          try {
    86              bundle = loadBundle (bundleName, loader);
    87          }
    88          catch (Throwable e) {
    89              failure = e;
    90          }
    91      }
    92
    93      /** An instance bound to a bundle. This method uses the current class
    94       * loader to find the bundle.
    95       * @param bundleName the name of the bundle
    96       * @return the helper instance bound to the bundle
    97       */
    98      public static I18NHelper getInstance (String bundleName) {
    99          return getInstance (bundleName, I18NHelper.class.getClassLoader());
    100     }
    101
    102     /** An instance bound to a bundle. This method figures out the bundle name
    103      * for the class object's package and uses the class' class loader to
    104      * find the bundle. Note, the specified class object must not be
    105      * <code>null</code>.
    106      * @param cls the class object from which to load the resource bundle
    107      * @return the helper instance bound to the bundle
    108      */
    109     public static I18NHelper getInstance (final Class cls) {
    110         ClassLoader classLoader = AccessController.doPrivileged (
    111             new PrivilegedAction<ClassLoader> () {
    112                 public ClassLoader run () {
    113                     return cls.getClassLoader();
    114                 }
    115             }
    116             );
    117         String bundle = getPackageName (cls.getName()) + bundleSuffix;
    118         return getInstance (bundle, classLoader);
    119     }
    120
    121     /** An instance bound to a bundle. This method uses the specified class
    122      * loader to find the bundle. Note, the specified class loader must not
    123      * be <code>null</code>.
    124      * @param bundleName the name of the bundle
    125      * @param loader the class loader from which to load the resource
    126      * bundle
    127      * @return the helper instance bound to the bundle
    128      */
    129     public static I18NHelper getInstance (String bundleName,
    130                                           ClassLoader loader) {
    131         I18NHelper helper = (I18NHelper) helpers.get (bundleName);
    132         if (helper != null) {
    133             return helper;
    134         }
    135         helper = new I18NHelper(bundleName, loader);
    136         helpers.put (bundleName, helper);
    137         // if two threads simultaneously create the same helper, return the first
    138         // one to be put into the Hashtable.  The other will be garbage collected.
    139         return (I18NHelper) helpers.get (bundleName);
    140     }
    141
    142     /** Message formatter
    143      * @param messageKey the message key
    144      * @return the resolved message text
    145      */
    146     public String msg (String messageKey) {
    147         assertBundle (messageKey);
    148         return getMessage (bundle, messageKey);
    149     }
    150
    151     /** Message formatter
    152      * @param messageKey the message key
    153      * @param arg1 the first argument
    154      * @return the resolved message text
    155      */
    156     public String msg (String messageKey, Object arg1) {
    157         assertBundle (messageKey);
    158         return getMessage (bundle, messageKey, arg1);
    159     }
    160
    161     /** Message formatter
    162      * @param messageKey the message key
    163      * @param arg1 the first argument
    164      * @param arg2 the second argument
    165      * @return the resolved message text
    166      */
    167     public String msg (String messageKey, Object arg1, Object arg2) {
    168         assertBundle (messageKey);
    169         return getMessage (bundle, messageKey, arg1, arg2);
    170     }
    171
    172     /** Message formatter
    173      * @param messageKey the message key
    174      * @param arg1 the first argument
    175      * @param arg2 the second argument
    176      * @param arg3 the third argument
    177      * @return the resolved message text
    178      */
    179     public String msg (String messageKey, Object arg1, Object arg2, Object arg3) {
    180         assertBundle (messageKey);
    181         return getMessage (bundle, messageKey, arg1, arg2, arg3);
    182     }
    183
    184     /** Message formatter
    185      * @param messageKey the message key
    186      * @param args the array of arguments
    187      * @return the resolved message text
    188      */
    189     public String msg (String messageKey, Object[] args) {
    190         assertBundle (messageKey);
    191         return getMessage (bundle, messageKey, args);
    192     }
    193
    194     /** Message formatter
    195      * @param messageKey the message key
    196      * @param arg the argument
    197      * @return the resolved message text
    198      */
    199     public String msg (String messageKey, int arg) {
    200         assertBundle (messageKey);
    201         return getMessage(bundle, messageKey, arg);
    202     }
    203
    204     /** Message formatter
    205      * @param messageKey the message key
    206      * @param arg the argument
    207      * @return the resolved message text
    208      */
    209     public String msg (String messageKey, boolean arg) {
    210         assertBundle (messageKey);
    211         return getMessage(bundle, messageKey, arg);
    212     }
    213
    214     /** Returns the resource bundle used by this I18NHelper.
    215      * @return the associated resource bundle
    216      * @since 1.1
    217      */
    218     public ResourceBundle getResourceBundle () {
    219         assertBundle ();
    220         return bundle;
    221     }
    222
    223     //========= Internal helper methods ==========
    224
    225     /**
    226      * Load ResourceBundle by bundle name
    227      * @param bundleName the name of the bundle
    228      * @param loader the class loader from which to load the resource bundle
    229      * @return  the ResourceBundle
    230      */
    231     final private static ResourceBundle loadBundle(
    232         String bundleName, ClassLoader loader) {
    233         ResourceBundle messages = (ResourceBundle)bundles.get(bundleName);
    234
    235         if (messages == null) //not found as loaded - add
    236         {
    237             if (loader != null) {
    238                 messages = ResourceBundle.getBundle(bundleName, locale, loader);
    239             } else {
    240                 // the JDO library is loaded by the boostrap class loader
    241                 messages = ResourceBundle.getBundle(bundleName, locale,
    242                         getSystemClassLoaderPrivileged());
    243             }
    244             bundles.put(bundleName, messages);
    245         }
    246         return messages;
    247     }
    248
    249     /** Assert resources available
    250      * @since 1.1
    251      * @throws JDOFatalInternalException if the resource bundle could not
    252      * be loaded during construction.
    253      */
    254     private void assertBundle () {
    255         if (failure != null)
    256             throw new JDOFatalInternalException (
    257                 "No resources could be found for bundle:\"" +
    258                 bundle + "\" ", failure);
    259     }
    260
    261     /** Assert resources available
    262      * @param key the message key
    263      * @since 1.0.2
    264      * @throws JDOFatalInternalException if the resource bundle could not
    265      * be loaded during construction.
    266      */
    267     private void assertBundle (String key) {
    268         if (failure != null)
    269             throw new JDOFatalInternalException (
    270                 "No resources could be found to annotate error message key:\"" +
    271                 key + "\"", failure);
    272     }
    273
    274     /**
    275      * Returns message as <code>String</code>
    276      * @param messages the resource bundle
    277      * @param messageKey the message key
    278      * @return the resolved message text
    279      */
    280     final private static String getMessage(ResourceBundle messages, String messageKey)
    281     {
    282         return messages.getString(messageKey);
    283     }
    284
    285     /**
    286      * Formats message by adding array of arguments
    287      * @param messages the resource bundle
    288      * @param messageKey the message key
    289      * @param msgArgs an array of arguments to substitute into the message
    290      * @return the resolved message text
    291      */
    292     final private static String getMessage(ResourceBundle messages,
    293             String messageKey, Object[] msgArgs)
    294     {
    295         for (int i=0; i<msgArgs.length; i++) {
    296             if (msgArgs[i] == null) msgArgs[i] = ""; // NOI18N
    297         }
    298         MessageFormat formatter = new MessageFormat(messages.getString(messageKey));
    299         return formatter.format(msgArgs);
    300     }
    301
    302     /**
    303      * Formats message by adding an <code>Object</code> argument.
    304      * @param messages the resource bundle
    305      * @param messageKey the message key
    306      * @param arg the argument
    307      * @return the resolved message text
    308      */
    309     final private static String getMessage(ResourceBundle messages,
    310             String messageKey, Object arg)
    311     {
    312         Object []args = {arg};
    313         return getMessage(messages, messageKey, args);
    314     }
    315
    316     /**
    317      * Formats message by adding two <code>Object</code> arguments.
    318      * @param messages the resource bundle
    319      * @param messageKey the message key
    320      * @param arg1 the first argument
    321      * @param arg2 the second argument
    322      * @return the resolved message text
    323      */
    324     final private static String getMessage(ResourceBundle messages,
    325             String messageKey, Object arg1, Object arg2)
    326     {
    327         Object []args = {arg1, arg2};
    328         return getMessage(messages, messageKey, args);
    329     }
    330
    331     /**
    332      * Formats message by adding three <code>Object</code> arguments.
    333      * @param messages the resource bundle
    334      * @param messageKey the message key
    335      * @param arg1 the first argument
    336      * @param arg2 the second argument
    337      * @param arg3 the third argument
    338      * @return the resolved message text
    339      */
    340     final private static String getMessage(ResourceBundle messages,
    341             String messageKey, Object arg1, Object arg2, Object arg3)
    342     {
    343         Object []args = {arg1, arg2, arg3};
    344         return getMessage(messages, messageKey, args);
    345     }
    346
    347     /**
    348      * Formats message by adding an <code>int</code> as an argument.
    349      * @param messages the resource bundle
    350      * @param messageKey the message key
    351      * @param arg the argument
    352      * @return the resolved message text
    353      */
    354     final private static String getMessage(ResourceBundle messages,
    355             String messageKey, int arg)
    356     {
    357         Object []args = {new Integer(arg)};
    358         return getMessage(messages, messageKey, args);
    359     }
    360
    361     /**
    362      * Formats message by adding a <code>boolean</code> as an argument.
    363      * @param messages the resource bundle
    364      * @param messageKey the message key
    365      * @param arg the argument
    366      * @return the resolved message text
    367      */
    368     final private static String getMessage(ResourceBundle messages,
    369             String messageKey, boolean arg)
    370     {
    371         Object []args = {String.valueOf(arg)};
    372         return getMessage(messages, messageKey, args);
    373     }
    374
    375     /**
    376      * Returns the package portion of the specified class.
    377      * @param className the name of the class from which to extract the
    378      * package
    379      * @return package portion of the specified class
    380      */
    381     final private static String getPackageName(final String className)
    382     {
    383         final int index = className.lastIndexOf('.');
    384         return ((index != -1) ? className.substring(0, index) : ""); // NOI18N
    385     }
    386
    387     /**
    388      * Get the system class loader. This must be done in a doPrivileged
    389      * block because of security.
    390      */
    391     private static ClassLoader getSystemClassLoaderPrivileged() {
    392         return AccessController.doPrivileged (
    393             new PrivilegedAction<ClassLoader> () {
    394                 public ClassLoader run () {
    395                     return ClassLoader.getSystemClassLoader();
    396                 }
    397             }
    398         );
    399     }
    400 }