You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@turbine.apache.org by he...@apache.org on 2003/04/07 17:42:16 UTC
cvs commit: jakarta-turbine-2/src/java/org/apache/turbine/services/localization TurbineLocalizationService.java
henning 2003/04/07 08:42:16
Modified: src/java/org/apache/turbine/services/localization
TurbineLocalizationService.java
Log:
- Backport multiple resource bundles patch from Fulcrum.
- fix log class
- Hashtable -> HashMap/Map
- use StringUtils
-
Revision Changes Path
1.9 +178 -42 jakarta-turbine-2/src/java/org/apache/turbine/services/localization/TurbineLocalizationService.java
Index: TurbineLocalizationService.java
===================================================================
RCS file: /home/cvs/jakarta-turbine-2/src/java/org/apache/turbine/services/localization/TurbineLocalizationService.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- TurbineLocalizationService.java 4 Apr 2003 14:59:29 -0000 1.8
+++ TurbineLocalizationService.java 7 Apr 2003 15:42:16 -0000 1.9
@@ -54,8 +54,9 @@
* <http://www.apache.org/>.
*/
-import java.util.Hashtable;
+import java.util.HashMap;
import java.util.Locale;
+import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
@@ -99,6 +100,7 @@
* @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
* @author <a href="mailto:novalidemail@foo.com">Frank Y. Kim</a>
* @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author <a href="mailto:leonardr@collab.net">Leonard Richardson</a>
* @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
* @version $Id$
*/
@@ -107,14 +109,21 @@
implements LocalizationService
{
/** Logging */
- private static Log log = LogFactory.getLog(LocalizationTool.class);
+ private static Log log = LogFactory.getLog(TurbineLocalizationService.class);
/**
- * The ResourceBundles in this service.
+ * The value to pass to <code>MessageFormat</code> if a
+ * <code>null</code> reference is passed to <code>format()</code>.
+ */
+ private static final Object[] NO_ARGS = new Object[0];
+
+ /**
+ * Bundle name keys a Map of the ResourceBundles in this
+ * service (which is in turn keyed by Locale).
* Key=bundle name
* Value=Hashtable containing ResourceBundles keyed by Locale.
*/
- private Hashtable bundles = null;
+ private Map bundles = null;
/**
* The list of default bundles to search.
@@ -138,7 +147,7 @@
*/
public TurbineLocalizationService()
{
- bundles = new Hashtable();
+ bundles = new HashMap();
}
/**
@@ -178,7 +187,7 @@
// Using old-style single bundle name property.
if (bundleNames == null || bundleNames.length <= 0)
{
- bundleNames = new String[]{name};
+ bundleNames = new String[] {name};
}
else
{
@@ -196,6 +205,22 @@
}
/**
+ * Retrieves the default language (specified in the config file).
+ */
+ public String getDefaultLanguage()
+ {
+ return defaultLanguage;
+ }
+
+ /**
+ * Retrieves the default country (specified in the config file).
+ */
+ public String getDefaultCountry()
+ {
+ return defaultCountry;
+ }
+
+ /**
* Retrieves the name of the default bundle (as specified in the
* config file).
* @see org.apache.turbine.services.localization.LocalizationService#getDefaultBundleName()
@@ -206,6 +231,14 @@
}
/**
+ * @see org.apache.turbine.services.localization.LocalizationService#getBundleNames()
+ */
+ public String[] getBundleNames()
+ {
+ return (String []) bundleNames.clone();
+ }
+
+ /**
* This method returns a ResourceBundle given the bundle name
* "DEFAULT" and the default Locale information supplied in
* TurbineProperties.
@@ -302,57 +335,159 @@
* This method returns a ResourceBundle for the given bundle name
* and the given Locale.
*
- * @param bundleName Name of bundle.
- * @param locale A Locale.
+ * @param bundleName Name of bundle (or <code>null</code> for the
+ * default bundle).
+ * @param locale The locale (or <code>null</code> for the locale
+ * indicated by the default language and country).
* @return A localized ResourceBundle.
*/
public ResourceBundle getBundle(String bundleName, Locale locale)
{
// Assure usable inputs.
- bundleName = (bundleName == null ? getDefaultBundleName() : bundleName.trim());
+ bundleName = (StringUtils.isEmpty(bundleName) ? getDefaultBundleName() : bundleName.trim());
if (locale == null)
{
locale = getLocale((String) null);
}
- if (bundles.containsKey(bundleName))
+ // Find/retrieve/cache bundle.
+ ResourceBundle rb = null;
+ Map bundlesByLocale = (Map) bundles.get(bundleName);
+ if (bundlesByLocale != null)
+ {
+ // Cache of bundles by locale for the named bundle exists.
+ // Check the cache for a bundle corresponding to locale.
+ rb = (ResourceBundle) bundlesByLocale.get(locale);
+
+ if (rb == null)
+ {
+ // Not yet cached.
+ rb = cacheBundle(bundleName, locale);
+ }
+ }
+ else
{
- Hashtable locales = (Hashtable) bundles.get(bundleName);
+ rb = cacheBundle(bundleName, locale);
+ }
+ return rb;
+ }
+
+ /**
+ * Caches the named bundle for fast lookups. This operation is
+ * relatively expesive in terms of memory use, but is optimized
+ * for run-time speed in the usual case.
+ *
+ * @exception MissingResourceException Bundle not found.
+ */
+ private synchronized ResourceBundle cacheBundle(String bundleName,
+ Locale locale)
+ throws MissingResourceException
+ {
+ Map bundlesByLocale = (HashMap) bundles.get(bundleName);
+ ResourceBundle rb = (bundlesByLocale == null ? null :
+ (ResourceBundle) bundlesByLocale.get(locale));
- if (locales.containsKey(locale))
+ if (rb == null)
+ {
+ bundlesByLocale = (bundlesByLocale == null ? new HashMap(3) :
+ new HashMap(bundlesByLocale));
+ try
{
- return (ResourceBundle) locales.get(locale);
+ rb = ResourceBundle.getBundle(bundleName, locale);
}
- else
+ catch (MissingResourceException e)
{
- // Try to create a ResourceBundle for this Locale.
- ResourceBundle rb = ResourceBundle.getBundle(bundleName,
- locale);
+ rb = findBundleByLocale(bundleName, locale, bundlesByLocale);
+ if (rb == null)
+ {
+ throw (MissingResourceException) e.fillInStackTrace();
+ }
+ }
- // Cache the ResourceBundle in memory.
- locales.put(rb.getLocale(), rb);
+ if (rb != null)
+ {
+ // Cache bundle.
+ bundlesByLocale.put(rb.getLocale(), rb);
- return rb;
+ Map bundlesByName = new HashMap(bundles);
+ bundlesByName.put(bundleName, bundlesByLocale);
+ this.bundles = bundlesByName;
}
}
- else
+ return rb;
+ }
+
+ /**
+ * <p>Retrieves the bundle most closely matching first against the
+ * supplied inputs, then against the defaults.</p>
+ *
+ * <p>Use case: some clients send a HTTP Accept-Language header
+ * with a value of only the language to use
+ * (i.e. "Accept-Language: en"), and neglect to include a country.
+ * When there is no bundle for the requested language, this method
+ * can be called to try the default country (checking internally
+ * to assure the requested criteria matches the default to avoid
+ * disconnects between language and country).</p>
+ *
+ * <p>Since we're really just guessing at possible bundles to use,
+ * we don't ever throw <code>MissingResourceException</code>.</p>
+ */
+ private ResourceBundle findBundleByLocale(String bundleName, Locale locale,
+ Map bundlesByLocale)
+ {
+ ResourceBundle rb = null;
+ if ( !StringUtils.isNotEmpty(locale.getCountry()) &&
+ defaultLanguage.equals(locale.getLanguage()) )
+ {
+ /*
+ log.debug("Requested language '" + locale.getLanguage() +
+ "' matches default: Attempting to guess bundle " +
+ "using default country '" + defaultCountry + '\'');
+ */
+ Locale withDefaultCountry = new Locale(locale.getLanguage(),
+ defaultCountry);
+ rb = (ResourceBundle) bundlesByLocale.get(withDefaultCountry);
+ if (rb == null)
+ {
+ rb = getBundleIgnoreException(bundleName, withDefaultCountry);
+ }
+ }
+ else if ( !StringUtils.isNotEmpty(locale.getLanguage()) &&
+ defaultCountry.equals(locale.getCountry()) )
{
- // Try to create a ResourceBundle for this Locale.
- ResourceBundle rb = ResourceBundle.getBundle(bundleName,
- locale);
-
- // Cache the ResourceBundle in memory.
- Hashtable ht = new Hashtable();
- ht.put(locale, rb);
-
- // Can't call getLocale(), because that is jdk2. This
- // needs to be changed back, since the above approach
- // caches extra Locale and Bundle objects.
- // ht.put( rb.getLocale(), rb );
+ Locale withDefaultLanguage = new Locale(defaultLanguage,
+ locale.getCountry());
+ rb = (ResourceBundle) bundlesByLocale.get(withDefaultLanguage);
+ if (rb == null)
+ {
+ rb = getBundleIgnoreException(bundleName, withDefaultLanguage);
+ }
+ }
- bundles.put(bundleName, ht);
+ if (rb == null && !defaultLocale.equals(locale))
+ {
+ rb = getBundleIgnoreException(bundleName, defaultLocale);
+ }
- return rb;
+ return rb;
+ }
+
+ /**
+ * Retrieves the bundle using the
+ * <code>ResourceBundle.getBundle(String, Locale)</code> method,
+ * returning <code>null</code> instead of throwing
+ * <code>MissingResourceException</code>.
+ */
+ private final ResourceBundle getBundleIgnoreException(String bundleName,
+ Locale locale)
+ {
+ try
+ {
+ return ResourceBundle.getBundle(bundleName, locale);
+ }
+ catch (MissingResourceException ignored)
+ {
+ return null;
}
}
@@ -374,7 +509,7 @@
{
if (bundleNames.length <= 0)
{
- bundleNames = new String[]{defaultBundle};
+ bundleNames = new String[] {defaultBundle};
}
}
}
@@ -448,12 +583,13 @@
if (value == null)
{
String loc = locale.toString();
- log.debug(LocalizationService.SERVICE_NAME +
- " noticed missing resource: " +
- "bundleName=" + bundleName + ", locale=" + loc +
- ", key=" + key);
+ String mesg = LocalizationService.SERVICE_NAME +
+ " noticed missing resource: " +
+ "bundleName=" + bundleName + ", locale=" + loc +
+ ", key=" + key;
+ log.debug(mesg);
// Text not found in requested or default bundles.
- throw new MissingResourceException(bundleName, loc, key);
+ throw new MissingResourceException(mesg, bundleName, key);
}
return value;
---------------------------------------------------------------------
To unsubscribe, e-mail: turbine-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: turbine-dev-help@jakarta.apache.org