You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by gd...@apache.org on 2002/01/03 22:57:22 UTC

cvs commit: xml-axis/java/src/org/apache/axis/utils RB.java

gdaniels    02/01/03 13:57:22

  Added:       java/src/org/apache/axis/utils RB.java
  Log:
  Utility class for dealing with internationalized string resources.
  
  This class deals with:
  
  1) Locales
  2) Automatically merging properties from "parent" files to "children" (see
      comments)
  3) Automatically searching the correct place in the classpath for messages,
      which allows us to organize messages in a much nicer way
  
  Originally written by Karl Moss at Macromedia, tweaked for Axis.
  
  Revision  Changes    Path
  1.1                  xml-axis/java/src/org/apache/axis/utils/RB.java
  
  Index: RB.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Axis" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.axis.utils;
  
  import java.util.HashMap;
  import java.util.ResourceBundle;
  import java.util.MissingResourceException;
  import java.util.Locale;
  import java.util.Hashtable;
  import java.util.Properties;
  import java.util.Date;
  import java.util.Enumeration;
  import java.text.MessageFormat;
  import java.io.InputStream;
  import java.io.IOException;
  
  /**
   * <p>Wrapper class for resource bundles. Property files are used to store
   * resource strings, which are the only types of resources available.
   * Property files can inherit properties from other files so that
   * a base property file can be used and a small number of properties
   * can be over-ridden by another property file. For example you may
   * create an english version of a resource file named "resource.properties".
   * You then decide that the British English version of all of the properties
   * except one are the same, so there is no need to redefine all of the
   * properties in "resource_en_GB", just the one that is different.</p>
   * <p>The property file lookup searches for classes with various suffixes
   * on the basis if the desired local and the current default local
   * (as returned by Local.getDefault()). As property files are found the
   * property values are merged so that inheritance is preserved.</p>
   * <p>The order of searching is:</p>
   * <dir>
   * basename + "_" + langage + "_" + country + "_" + variant
   * basename + "_" + langage + "_" + country
   * basename + "_" + langage
   * basename + "_" + defaultLanguage + "_" + defaultCountry + "_" + defaultVariant
   * basename + "_" + defaultLanguage + "_" + defaultCountry
   * basename + "_" + defaultLanguage
   * basename
   * </dir>
   * <p>The basename is the name of the property file without the ".properties"
   * extension.</p>
   * <p>Properties will be cached for performance.<p>
   * <p>Property values stored in the property files can also contain dynamic
   * variables. Any dynamic variable defined in PropertiesUtil.getVariableValue()
   * can be used (such as {date}), as well as arguments in the form {0}, {1}, etc.
   * Argument values are specified in the various overloaded getString() methods.</p>
   * 
   * @author Karl Moss (kmoss@macromedia.com)
   * @author Glen Daniels (gdaniels@macromedia.com)
   */
  public class RB {
      // The static cache of properties. The key is the basename + the local +
      // the default local and the element is the Properties object containing
      // the resources
      static Hashtable propertyCache = new Hashtable();
  
      // The default base name
      public static final String BASE_NAME = "resource";
  
      // The property file extension
      public static final String PROPERTY_EXT = ".properties";
  
      // The name of the current base property file (with extension)
      protected String basePropertyFileName;
  
      // The properties for the current resource bundle
      protected Properties resourceProperties;
  
      /**
        * Construct a new RB
        * @param name The name of the property file without the ".properties" extension
        */
      public RB(String name) throws MissingResourceException
      {
          this(null, name, null);
      }
  
      /**
        * Construct a new RB
        * @param caller The calling object. This is used to get the package name
        * to further construct the basename as well as to get the proper ClassLoader
        * @param name The name of the property file without the ".properties" extension
        */
      public RB(Object caller, String name) throws MissingResourceException
      {
          this(caller, name, null);
      }
  
      /**
        * Construct a new RB
        * @param caller The calling object. This is used to get the package name
        * to further construct the basename as well as to get the proper ClassLoader
        * @param name The name of the property file without the ".properties" extension
        * @param local The local
        */
      public RB(Object caller, String name, Locale locale) throws MissingResourceException
      {
          ClassLoader cl = null;
  
          if (caller != null) {
  
              Class c;
              if (caller instanceof Class) {
                  c = (Class) caller;
              }
              else {
                  c = caller.getClass();
              }
  
              // Get the appropriate class loader
              cl = c.getClassLoader();
  
              if (name.indexOf("/") == -1) {
  
                  // Create the full basename only if not given
                  String fullName = c.getName();
  
                  int pos = fullName.lastIndexOf(".");
                  if (pos > 0) {
                      name = fullName.substring(0, pos + 1).replace('.', '/') + name;
                  }
              }
          }
  
          Locale defaultLocale = Locale.getDefault();
  
          // If the locale given is the same as the default locale, ignore it
          if (locale != null) {
              if (locale.equals(defaultLocale)) {
                  locale = null;
              }
          }
  
          // Load the properties. If no property files exist then a
          // MissingResourceException will be thrown
          loadProperties(name, cl, locale, defaultLocale);
      }
  
      /**
        * Gets a string message from the resource bundle for the given key
        * @param key The resource key
        * @return The message
        */
      public String getString(String key) throws MissingResourceException
      {
          return getString(key, (Object[]) null);
      }
  
      /**
        * <p>Gets a string message from the resource bundle for the given key. The
        * message may contain variables that will be substituted with the given
        * arguments. Variables have the format:</p>
        * <dir>
        * This message has two variables: {0} and {1}
        * </dir>
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @return The message
        */
      public String getString(String key, Object arg0) throws MissingResourceException
      {
          Object[] o = new Object[1];
          o[0] = arg0;
          return getString(key, o);
      }
  
      /**
        * <p>Gets a string message from the resource bundle for the given key. The
        * message may contain variables that will be substituted with the given
        * arguments. Variables have the format:</p>
        * <dir>
        * This message has two variables: {0} and {1}
        * </dir>
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @return The message
        */
      public String getString(String key, Object arg0, Object arg1) throws MissingResourceException
      {
          Object[] o = new Object[2];
          o[0] = arg0;
          o[1] = arg1;
          return getString(key, o);
      }
  
      /**
        * <p>Gets a string message from the resource bundle for the given key. The
        * message may contain variables that will be substituted with the given
        * arguments. Variables have the format:</p>
        * <dir>
        * This message has two variables: {0} and {1}
        * </dir>
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @param arg2 The argument to place in variable {1}
        * @return The message
        */
      public String getString(String key, Object arg0, Object arg1, Object arg2) throws MissingResourceException
      {
          Object[] o = new Object[3];
          o[0] = arg0;
          o[1] = arg1;
          o[2] = arg2;
          return getString(key, o);
      }
  
      /**
        * <p>Gets a string message from the resource bundle for the given key. The
        * message may contain variables that will be substituted with the given
        * arguments. Variables have the format:</p>
        * <dir>
        * This message has two variables: {0} and {1}
        * </dir>
        * @param key The resource key
        * @param array An array of objects to place in corresponding variables
        * @return The message
        */
      public String getString(String key, Object[] array) throws MissingResourceException
      {
          String msg = null;
          if (resourceProperties != null) {
              msg = resourceProperties.getProperty(key);
          }
  
          if (msg == null) {
              throw new MissingResourceException("Can't find resource key \"" + key +
                                                 "\" in base name " + basePropertyFileName,
                                                 basePropertyFileName, key);
          }
  
          msg = MessageFormat.format(msg, array);
          return msg;
      }
  
      protected void loadProperties(String basename, ClassLoader loader, Locale locale,
                                    Locale defaultLocale)
          throws MissingResourceException
      {
          // Check the cache first
          String loaderName = "";
          if (loader != null) {
              loaderName = ":" + loader.hashCode();
          }
          String cacheKey = basename + ":" + locale + ":" + defaultLocale + loaderName;
          Properties p = (Properties) propertyCache.get(cacheKey);
          basePropertyFileName = basename + PROPERTY_EXT;
  
          if (p == null) {
              // The properties were not found in the cache. Search the given locale
              // first
              if (locale != null) {
                  p = loadProperties(basename, loader, locale, p);
              }
  
              // Search the default locale
              if (defaultLocale != null) {
                  p = loadProperties(basename, loader, defaultLocale, p);
              }
  
              // Search for the basename
              p = merge(p, loadProperties(basePropertyFileName, loader));
  
              if (p == null) {
                  throw new MissingResourceException("Can't find resource for base name " +
                                                     basePropertyFileName, basePropertyFileName, "");
              }
  
              // Cache the properties
              propertyCache.put(cacheKey, p);
  
          }
  
          resourceProperties = p;
      }
  
      protected Properties loadProperties(String basename, ClassLoader loader, Locale locale,
                                          Properties props)
      {
  
          String language = locale.getLanguage();
          String country = locale.getCountry();
          String variant = locale.getVariant();
          if (variant != null) {
              if (variant.trim().length() == 0) {
                  variant = null;
              }
          }
  
          if (language != null) {
  
              if (country != null) {
  
                  if (variant != null) {
                      props = merge(props, loadProperties(basename + "_" + language +"_" + country + "_" + variant +
                                                          PROPERTY_EXT, loader));
                  }
                  props = merge(props, loadProperties(basename + "_" + language +"_" + country +
                                                      PROPERTY_EXT, loader));
              }
              props = merge(props, loadProperties(basename + "_" + language + PROPERTY_EXT, loader));
          }
          return props;
      }
  
      protected Properties loadProperties(String resname, ClassLoader loader)
      {
          Properties props = null;
  
          // Attempt to open and load the properties
          InputStream in = null;
          try {
              if (loader != null) {
                  in = loader.getResourceAsStream(resname);
              }
  
              // Either we're using the system class loader or we didn't find the
              // resource using the given class loader
              if (in == null) {
                  in = ClassLoader.getSystemResourceAsStream(resname);
              }
              if (in != null) {
                  props = new Properties();
                  try {
                      props.load(in);
                  }
                  catch (IOException ex) {
                      // On error, clear the props
                      props = null;
                  }
              }
          }
          finally {
              if (in != null) {
                  try {
                      in.close();
                  }
                  catch (Exception ex) {
                      // Ignore error on close
                  }
              }
          }
          return props;
      }
  
      /**
        * Merge two Properties objects
        */
      protected Properties merge(Properties p1, Properties p2)
      {
          if ((p1 == null) &&
              (p2 == null)) {
              return null;
          }
          else if (p1 == null) {
              return p2;
          }
          else if (p2 == null) {
              return p1;
          }
  
          // Now merge. p1 takes precedence
          Enumeration enum = p2.keys();
          while (enum.hasMoreElements()) {
              String key = (String) enum.nextElement();
              if (p1.getProperty(key) == null) {
                  p1.put(key, p2.getProperty(key));
              }
          }
  
          return p1;
      }
  
      /**
        * Get the underlying properties
        */
      public Properties getProperties()
      {
          return resourceProperties;
      }
  
      // STATIC ACCESSORS
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param key The resource key
        * @return The formatted message
        */
      public static String getString(Object caller, String key)
          throws MissingResourceException
      {
          return getMessage(caller, BASE_NAME, null, key, null);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @return The formatted message
        */
      public static String getString(Object caller, String key, Object arg0)
          throws MissingResourceException
      {
          Object[] o = new Object[1];
          o[0] = arg0;
          return getMessage(caller, BASE_NAME, null, key, o);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @return The formatted message
        */
      public static String getString(Object caller, String key, Object arg0, Object arg1)
          throws MissingResourceException
      {
          Object[] o = new Object[2];
          o[0] = arg0;
          o[1] = arg1;
          return getMessage(caller, BASE_NAME, null, key, o);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @param arg2 The argument to place in variable {2}
        * @return The formatted message
        */
      public static String getString(Object caller, String key, Object arg0, Object arg1, Object arg2)
          throws MissingResourceException
      {
          Object[] o = new Object[3];
          o[0] = arg0;
          o[1] = arg1;
          o[2] = arg2;
          return getMessage(caller, BASE_NAME, null, key, o);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @param arg2 The argument to place in variable {2}
        * @param arg3 The argument to place in variable {3}
        * @return The formatted message
        */
      public static String getString(Object caller, String key, Object arg0, Object arg1, Object arg2, Object arg3)
          throws MissingResourceException
      {
          Object[] o = new Object[4];
          o[0] = arg0;
          o[1] = arg1;
          o[2] = arg2;
          o[3] = arg3;
          return getMessage(caller, BASE_NAME, null, key, o);
      }
  
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @param arg2 The argument to place in variable {2}
        * @param arg3 The argument to place in variable {3}
        * @param arg4 The argument to place in variable {4}
        * @return The formatted message
        */
      public static String getString(Object caller, String key, Object arg0, Object arg1, Object arg2, Object arg3, Object arg4)
          throws MissingResourceException
      {
          Object[] o = new Object[5];
          o[0] = arg0;
          o[1] = arg1;
          o[2] = arg2;
          o[3] = arg3;
          o[4] = arg4;
          return getMessage(caller, BASE_NAME, null, key, o);
      }
  
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param key The resource key
        * @param array An array of objects to place in corresponding variables
        * @return The formatted message
        */
      public static String getString(Object caller, String key, Object[] args)
          throws MissingResourceException
      {
          return getMessage(caller, BASE_NAME, null, key, args);
      }
  
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param locale The locale
        * @param key The resource key
        * @return The formatted message
        */
      public static String getString(Object caller, Locale locale, String key)
          throws MissingResourceException
      {
          return getMessage(caller, BASE_NAME, locale, key, null);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param locale The locale
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @return The formatted message
        */
      public static String getString(Object caller, Locale locale, String key, Object arg0)
          throws MissingResourceException
      {
          Object[] o = new Object[1];
          o[0] = arg0;
          return getMessage(caller, BASE_NAME, locale, key, o);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param locale The locale
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @return The formatted message
        */
      public static String getString(Object caller, Locale locale, String key, Object arg0, Object arg1)
          throws MissingResourceException
      {
          Object[] o = new Object[2];
          o[0] = arg0;
          o[1] = arg1;
          return getMessage(caller, BASE_NAME, locale, key, o);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param locale The locale
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @param arg2 The argument to place in variable {2}
        * @return The formatted message
        */
      public static String getString(Object caller, Locale locale, String key, Object arg0, Object arg1, Object arg2)
          throws MissingResourceException
      {
          Object[] o = new Object[3];
          o[0] = arg0;
          o[1] = arg1;
          o[2] = arg2;
          return getMessage(caller, BASE_NAME, locale, key, o);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param locale The locale
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @param arg2 The argument to place in variable {2}
        * @param arg3 The argument to place in variable {3}
        * @return The formatted message
        */
      public static String getString(Object caller, Locale locale, String key, Object arg0, Object arg1, Object arg2, Object arg3)
          throws MissingResourceException
      {
          Object[] o = new Object[4];
          o[0] = arg0;
          o[1] = arg1;
          o[2] = arg2;
          o[3] = arg3;
          return getMessage(caller, BASE_NAME, locale, key, o);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param locale The locale
        * @param key The resource key
        * @param arg0 The argument to place in variable {0}
        * @param arg1 The argument to place in variable {1}
        * @param arg2 The argument to place in variable {2}
        * @param arg3 The argument to place in variable {3}
        * @return The formatted message
        */
      public static String getString(Object caller, Locale locale, String key, Object arg0, Object arg1, Object arg2, Object arg3, Object arg4)
          throws MissingResourceException
      {
          Object[] o = new Object[5];
          o[0] = arg0;
          o[1] = arg1;
          o[2] = arg2;
          o[3] = arg3;
          o[4] = arg4;
          return getMessage(caller, BASE_NAME, locale, key, o);
      }
  
      /**
        * Get a message from resource.properties from the package of the given object.
        * @param caller The calling object, used to get the package name and class loader
        * @param locale The locale
        * @param key The resource key
        * @param array An array of objects to place in corresponding variables
        * @return The formatted message
        */
      public static String getString(Object caller, Locale locale, String key, Object[] args)
          throws MissingResourceException
      {
          return getMessage(caller, BASE_NAME, locale, key, args);
      }
  
      // Workhorse that does the resource loading and key lookup
      public static String getMessage(Object caller, String basename, Locale locale, String key,
                                         Object[] args)
          throws MissingResourceException
      {
          String msg = null;
          MissingResourceException firstEx = null;
          String fullName = null;
          Class curClass = null;
          if (caller != null) {
              if(caller instanceof Class)
                  curClass = (Class) caller;
              else
                  curClass = caller.getClass();
          }
  
          while (msg == null) {
  
              // Get the full name of the resource
              if (curClass != null) {
  
                  // Create the full basename
                  String pkgName = curClass.getName();
  
                  int pos = pkgName.lastIndexOf(".");
                  if (pos > 0) {
                      fullName = pkgName.substring(0, pos + 1).replace('.', '/') + basename;
                  }
                  else {
                      fullName = basename;
                  }
              }
              else {
                  fullName = basename;
              }
  
              try {
                  RB rb = new RB(caller, fullName, locale);
                  msg = rb.getString(key, args);
              }
              catch (MissingResourceException ex) {
                  if (curClass == null) {
                      throw ex;
                  }
  
                  // Save the first exception
                  if (firstEx == null) {
                      firstEx = ex;
                  }
  
                  // Get the superclass
                  Class sc = curClass.getSuperclass();
                  if (sc == null) {
                      throw firstEx;
                  }
                  String cname = sc.getName();
                  if (cname.startsWith("java.") ||
                      cname.startsWith("javax.")) {
                      throw firstEx;
                  }
  
                  // Try the superclass package
                  curClass = sc;
              }
  
          }
          return msg;
      }
  
      /**
        * Clears the internal cache
        */
      public static void clearCache()
      {
          propertyCache.clear();
      }
  }