Author: niallp Date: Wed Nov 29 04:16:15 2006 New Revision: 480549 URL: http://svn.apache.org/viewvc?view=rev&rev=480549 Log: STR-2925 and STR-2077 - Add configuration option to PropertyMessageResources to operate in one of three modes: 1) default (as its always has) 2) JSTL compatible and 3) PropertyResourceBundle compatible
Added: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo.properties (with props) struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de.properties (with props) struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de_DE.properties (with props) struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en.properties (with props) struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en_US.properties (with props) struts/struts1/trunk/core/src/test/java/org/apache/struts/util/TestPropertyMessageResources.java (with props) Modified: struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config-i18nVariables.xml struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config.xml struts/struts1/trunk/core/pom.xml struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResources.java struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResourcesFactory.java struts/struts1/trunk/src/site/xdoc/userGuide/configuration.xml Modified: struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config-i18nVariables.xml URL: http://svn.apache.org/viewvc/struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config-i18nVariables.xml?view=diff&rev=480549&r1=480548&r2=480549 ============================================================================== --- struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config-i18nVariables.xml (original) +++ struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config-i18nVariables.xml Wed Nov 29 04:16:15 2006 @@ -104,9 +104,12 @@ automatically 'escape' single quotes. --> - <message-resources key="i18nExample" null="false" parameter="org.apache.struts.webapp.validator.I18nExample" /> + <message-resources key="i18nExample" null="false" parameter="org.apache.struts.webapp.validator.I18nExample"> + <set-property key="mode" value="JSTL"/> + </message-resources> <message-resources key="i18nVariables" null="true" parameter="org.apache.struts.webapp.validator.I18nExampleVariables" > <set-property property="escape" value="false"/> + <set-property key="mode" value="JSTL"/> </message-resources> </struts-config> Modified: struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config.xml URL: http://svn.apache.org/viewvc/struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config.xml?view=diff&rev=480549&r1=480548&r2=480549 ============================================================================== --- struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config.xml (original) +++ struts/struts1/trunk/apps/examples/src/main/webapp/WEB-INF/validator/struts-config.xml Wed Nov 29 04:16:15 2006 @@ -89,7 +89,9 @@ <!-- ===================================== Message Resources Definitions --> - <message-resources parameter="org.apache.struts.webapp.validator.MessageResources" /> + <message-resources parameter="org.apache.struts.webapp.validator.MessageResources"> + <set-property key="mode" value="JSTL"/> + </message-resources> <!-- ============================================ Plug Ins Configuration --> Modified: struts/struts1/trunk/core/pom.xml URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/pom.xml?view=diff&rev=480549&r1=480548&r2=480549 ============================================================================== --- struts/struts1/trunk/core/pom.xml (original) +++ struts/struts1/trunk/core/pom.xml Wed Nov 29 04:16:15 2006 @@ -67,6 +67,7 @@ <directory>src/test/java</directory> <includes> <include>**/*.xml</include> + <include>**/*.properties</include> </includes> </testResource> </testResources> Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResources.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResources.java?view=diff&rev=480549&r1=480548&r2=480549 ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResources.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResources.java Wed Nov 29 04:16:15 2006 @@ -33,8 +33,8 @@ /** * Concrete subclass of <code>MessageResources</code> that reads message keys - * and corresponding strings from named property resources in the same manner - * that <code>java.util.PropertyResourceBundle</code> does. The + * and corresponding strings from named property resources in a <b><i>similar</i></b> manner + * (see <i>modes</i> below) that <code>java.util.PropertyResourceBundle</code> does. The * <code>base</code> property defines the base property resource name, and * must be specified. <p> <strong>IMPLEMENTATION NOTE</strong> - This class * trades memory for speed by caching all messages located via generalizing @@ -43,10 +43,89 @@ * response time on subsequent requests for the same locale + key * combination. * - * @version $Rev$ $Date: 2005-05-07 12:11:38 -0400 (Sat, 07 May 2005) - * $ + * <h2>Operating Modes</h2> + * This implementation can be configured to operate in one of three modes: + * <ul> + * <li>1. <b>Default</b> - default, backwardly compatible, Struts behaviour (i.e. the way + * its always worked).</li> + * <li>2. <b>JSTL</b> - compatible with how JSTL finds messages + * (fix for <a href="http://issues.apache.org/struts/browse/STR-2925">STR-2925</a>)</li> + * <li>3. <b>Resource</b> - compatible with how Java's <code>PropertyResourceBundle</code> + * finds messages (fix for + * <a href="http://issues.apache.org/struts/browse/STR-2077">STR-2077</a>)</li> + * </ul> + * + * <h3>1. Default Mode</h3> + * <i>Default mode</i> is the way this implementation has always operated. It searches + * for a message key for property resources in the following sequence: + * <pre> + * base + "_" + localeLanguage + "_" + localeCountry + "_" + localeVariant + * base + "_" + localeLanguage + "_" + localeCountry + * base + "_" + localeLanguage + * base + "_" + default locale + * base + * </pre> + * <p> + * This mode is the <i>default</i> and requires no additional configuration. + * + * <h3>2. JSTL Mode</h3> + * <i>JSTL mode</i> is compatible with how JSTL operates and the default Locale + * is not used when looking for a message key. <i>JSTL mode</i> searches for + * a message key for property resources in the following sequence: + * <pre> + * base + "_" + localeLanguage + "_" + localeCountry + "_" + localeVariant + * base + "_" + localeLanguage + "_" + localeCountry + * base + "_" + localeLanguage + * base + * </pre> + * <p> + * Configure <code>PropertyMessageResources</code> to operate in this mode by + * specifying a value of <code>JSTL</code> for the <code>mode</code> + * key in your <code>struts-config.xml</code>: + * <pre> + * <message-resources parameter="mypackage.MyMessageResources"> + * <set-property key="mode" value="JSTL"/> + * </message-resources> + * </pre> + * + * <h3>3. Resource Mode</h3> + * <i>Resource mode</i> is compatible with how Java's <code>PropertyResourceBundle</code> + * operates. <i>Resource mode</i> searches first through the specified Locale's language, + * country and variant, then through the default Locale's language, + * country and variant and finally using just the <code>base</code>: + * <pre> + * base + "_" + localeLanguage + "_" + localeCountry + "_" + localeVariant + * base + "_" + localeLanguage + "_" + localeCountry + * base + "_" + localeLanguage + * base + "_" + defaultLanguage + "_" + defaultCountry + "_" + defaultVariant + * base + "_" + defaultLanguage + "_" + defaultCountry + * base + "_" + defaultLanguage + * base + * </pre> + * <p> + * Configure <code>PropertyMessageResources</code> to operate in this mode by + * specifying a value of <code>resource</code> for the <code>mode</code> + * key in your <code>struts-config.xml</code>: + * <pre> + * <message-resources parameter="mypackage.MyMessageResources"> + * <set-property key="mode" value="resource"/> + * </message-resources> + * </pre> + * + * @version $Rev$ $Date$ */ public class PropertyMessageResources extends MessageResources { + + + /** Indicates compatibility with how PropertyMessageResources has always looked up messages */ + private static final int MODE_DEFAULT = 0; + + /** Indicates compatibility with how JSTL looks up messages */ + private static final int MODE_JSTL = 1; + + /** Indicates compatibility with how java's PropertyResourceBundle looks up messages */ + private static final int MODE_RESOURCE_BUNDLE = 2; + /** * The <code>Log</code> instance for this class. */ @@ -67,6 +146,11 @@ */ protected HashMap messages = new HashMap(); + /** + * Compatibility mode that PropertyMessageResources is operating in. + */ + private int mode = MODE_DEFAULT; + // ----------------------------------------------------------- Constructors /** @@ -101,8 +185,35 @@ // --------------------------------------------------------- Public Methods /** - * Returns a text message for the specified key, for the default Locale. A - * null string result will be returned by this method if no relevant + * Set the compatibility mode this implementation uses for message lookup. + * + * @param mode <code>JSTL</code> for JSTL compatibility, + * <code>resource</code> for PropertyResourceBundle compatibility or + * <code>default</code> for Struts backward compatibility. + */ + public void setMode(String mode) { + String value = (mode == null ? null : mode.trim()); + if ("jstl".equalsIgnoreCase(value)) { + this.mode = MODE_JSTL; + if (log.isDebugEnabled()) { + log.info("Operating in JSTL compatible mode [" + mode + "]"); + } + } else if ("resource".equalsIgnoreCase(value)) { + this.mode = MODE_RESOURCE_BUNDLE; + if (log.isDebugEnabled()) { + log.info("Operating in PropertyResourceBundle compatible mode [" + mode + "]"); + } + } else { + this.mode = MODE_DEFAULT; + if (log.isDebugEnabled()) { + log.info("Operating in Default mode [" + mode + "]"); + } + } + } + + /** + * Returns a text message for the specified key, for the specified or default + * Locale. A null string result will be returned by this method if no relevant * message resource is found for this key or Locale, if the * <code>returnNull</code> property is set. Otherwise, an appropriate * error message will be returned. <p> This method must be implemented by @@ -121,72 +232,45 @@ // Initialize variables we will require String localeKey = localeKey(locale); String originalKey = messageKey(localeKey, key); - String messageKey = null; String message = null; - int underscore = 0; - boolean addIt = false; // Add if not found under the original key - // Loop from specific to general Locales looking for this message - while (true) { - // Load this Locale's messages if we have not done so yet - loadLocale(localeKey); + // Search the specified Locale + message = findMessage(locale, key, originalKey); + if (message != null) { + return message; + } - // Check if we have this key for the current locale key - messageKey = messageKey(localeKey, key); + // JSTL Compatibility - JSTL doesn't use the default locale + if (mode == MODE_JSTL) { - synchronized (messages) { - message = (String) messages.get(messageKey); + // do nothing (i.e. don't use default Locale) - if (message != null) { - if (addIt) { - messages.put(originalKey, message); - } + // PropertyResourcesBundle - searches through the hierarchy + // for the default Locale (e.g. first en_US then en) + } else if (mode == MODE_RESOURCE_BUNDLE) { - return (message); - } + if (!defaultLocale.equals(locale)) { + message = findMessage(defaultLocale, key, originalKey); } - // Strip trailing modifiers to try a more general locale key - addIt = true; - underscore = localeKey.lastIndexOf("_"); + // Default (backwards) Compatibility - just searches the + // specified Locale (e.g. just en_US) + } else { - if (underscore < 0) { - break; + if (!defaultLocale.equals(locale)) { + localeKey = localeKey(defaultLocale); + message = findMessage(localeKey, key, originalKey); } - localeKey = localeKey.substring(0, underscore); } - - // Try the default locale if the current locale is different - if (!defaultLocale.equals(locale)) { - localeKey = localeKey(defaultLocale); - messageKey = messageKey(localeKey, key); - loadLocale(localeKey); - - synchronized (messages) { - message = (String) messages.get(messageKey); - - if (message != null) { - messages.put(originalKey, message); - - return (message); - } - } + if (message != null) { + return message; } - // As a last resort, try the default Locale - localeKey = ""; - messageKey = messageKey(localeKey, key); - loadLocale(localeKey); - - synchronized (messages) { - message = (String) messages.get(messageKey); - - if (message != null) { - messages.put(originalKey, message); - - return (message); - } + // Find the message in the default properties file + message = findMessage("", key, originalKey); + if (message != null) { + return message; } // Return an appropriate error indication @@ -288,6 +372,85 @@ messages.put(messageKey(localeKey, key), props.getProperty(key)); } + } + } + + // -------------------------------------------------------- Private Methods + + /** + * Returns a text message for the specified key, for the specified Locale. + * <p> + * A null string result will be returned by this method if no relevant + * message resource is found. This method searches through the locale + * <i>hierarchy</i> (i.e. variant --> languge --> country) for the message. + * + * @param locale The requested message Locale, or <code>null</code> for + * the system default Locale + * @param key The message key to look up + * @param originalKey The original message key to cache any found message under + * @return text message for the specified key and locale + */ + private String findMessage(Locale locale, String key, String originalKey) { + + // Initialize variables we will require + String localeKey = localeKey(locale); + String messageKey = null; + String message = null; + int underscore = 0; + + // Loop from specific to general Locales looking for this message + while (true) { + message = findMessage(localeKey, key, originalKey); + if (message != null) { + break; + } + + // Strip trailing modifiers to try a more general locale key + underscore = localeKey.lastIndexOf("_"); + + if (underscore < 0) { + break; + } + + localeKey = localeKey.substring(0, underscore); + } + + return message; + + } + + /** + * Returns a text message for the specified key, for the specified Locale. + * <p> + * A null string result will be returned by this method if no relevant + * message resource is found. + * + * @param locale The requested key of the Locale + * @param key The message key to look up + * @param originalKey The original message key to cache any found message under + * @return text message for the specified key and locale + */ + private String findMessage(String localeKey, String key, String originalKey) { + + // Load this Locale's messages if we have not done so yet + loadLocale(localeKey); + + // Check if we have this key for the current locale key + String messageKey = messageKey(localeKey, key); + + // Add if not found under the original key + boolean addIt = !messageKey.equals(originalKey); + + synchronized (messages) { + String message = (String) messages.get(messageKey); + + if (message != null) { + if (addIt) { + messages.put(originalKey, message); + } + + } + return (message); } } } Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResourcesFactory.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResourcesFactory.java?view=diff&rev=480549&r1=480548&r2=480549 ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResourcesFactory.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/util/PropertyMessageResourcesFactory.java Wed Nov 29 04:16:15 2006 @@ -26,8 +26,7 @@ * configuration paramter for such instances is the base Java package name of * the resources entries from which our keys and values will be loaded. * - * @version $Rev$ $Date: 2005-05-07 12:11:38 -0400 (Sat, 07 May 2005) - * $ + * @version $Rev$ $Date$ */ public class PropertyMessageResourcesFactory extends MessageResourcesFactory { // --------------------------------------------------------- Public Methods @@ -39,6 +38,13 @@ * @param config Configuration parameter(s) for the requested bundle */ public MessageResources createResources(String config) { - return new PropertyMessageResources(this, config, this.returnNull); + PropertyMessageResources messageResources = + new PropertyMessageResources(this, config, this.returnNull); + String mode = null; + if (getConfig() != null) { + mode = getConfig().getProperty("mode"); + } + messageResources.setMode(mode); + return messageResources; } } Added: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo.properties URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo.properties?view=auto&rev=480549 ============================================================================== --- struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo.properties (added) +++ struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo.properties Wed Nov 29 04:16:15 2006 @@ -0,0 +1,5 @@ +key.all=ALL default +key.default=default only +key.lang=LANG default +key.country=COUNTRY default + Propchange: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo.properties ------------------------------------------------------------------------------ svn:eol-style = native Added: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de.properties URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de.properties?view=auto&rev=480549 ============================================================================== --- struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de.properties (added) +++ struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de.properties Wed Nov 29 04:16:15 2006 @@ -0,0 +1,3 @@ +key.all=ALL de +key.de=de only +key.lang=LANG de Propchange: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de.properties ------------------------------------------------------------------------------ svn:eol-style = native Added: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de_DE.properties URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de_DE.properties?view=auto&rev=480549 ============================================================================== --- struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de_DE.properties (added) +++ struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de_DE.properties Wed Nov 29 04:16:15 2006 @@ -0,0 +1,3 @@ +key.all=ALL de_DE +key.de_DE=de_DE only +key.country=COUNTRY de_DE Propchange: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_de_DE.properties ------------------------------------------------------------------------------ svn:eol-style = native Added: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en.properties URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en.properties?view=auto&rev=480549 ============================================================================== --- struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en.properties (added) +++ struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en.properties Wed Nov 29 04:16:15 2006 @@ -0,0 +1,3 @@ +key.all=ALL en +key.en=en only +key.lang=LANG en Propchange: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en.properties ------------------------------------------------------------------------------ svn:eol-style = native Added: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en_US.properties URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en_US.properties?view=auto&rev=480549 ============================================================================== --- struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en_US.properties (added) +++ struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en_US.properties Wed Nov 29 04:16:15 2006 @@ -0,0 +1,3 @@ +key.all=ALL en_US +key.en_US=en_US only +key.country=COUNTRY en_US Propchange: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/Foo_en_US.properties ------------------------------------------------------------------------------ svn:eol-style = native Added: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/TestPropertyMessageResources.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/test/java/org/apache/struts/util/TestPropertyMessageResources.java?view=auto&rev=480549 ============================================================================== --- struts/struts1/trunk/core/src/test/java/org/apache/struts/util/TestPropertyMessageResources.java (added) +++ struts/struts1/trunk/core/src/test/java/org/apache/struts/util/TestPropertyMessageResources.java Wed Nov 29 04:16:15 2006 @@ -0,0 +1,220 @@ +/* + * $Id$ + * + * 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. + */ +package org.apache.struts.util; + +import junit.framework.Test; +import junit.framework.TestSuite; +import junit.framework.TestCase; + +import java.util.Locale; +import org.apache.struts.config.MessageResourcesConfig; + +/** + * Unit tests for PropertyMessageResources. + * + * @version $Revision$ + */ +public class TestPropertyMessageResources extends TestCase { + + + private static final String FOO_RESOURCES = "org.apache.struts.util.Foo"; + + private Locale defaultLocale; + + // ----------------------------------------------------------------- Basics + public TestPropertyMessageResources(String name) { + super(name); + } + + public static void main(String[] args) { + junit.awtui.TestRunner.main(new String[] { + TestPropertyMessageResources.class.getName() + }); + } + + public static Test suite() { + return (new TestSuite(TestPropertyMessageResources.class)); + } + + // ----------------------------------------------------- Setup and Teardown + public void setUp() { + // cache the default locale + defaultLocale = Locale.getDefault(); + } + + public void tearDown() { + // restore the default locale + Locale.setDefault(defaultLocale); + } + + // ------------------------------------------------------- Individual Tests + + /** + * Test Struts default PropertyMessageResources behaviour + */ + public void testDefaultMode() { + + Locale.setDefault(Locale.US); + + // Create message resources - default Struts Behaviour +// MessageResources resources = createMessageResources(FOO_RESOURCES, true, "DEFAULT"); + MessageResources resources = createMessageResources(FOO_RESOURCES, true, null); + + // Test language (& default) only keys + assertEquals("key.lang FRANCE", "LANG default", resources.getMessage(Locale.FRANCE, "key.lang")); // no cached en_US + assertEquals("key.lang English", "LANG en", resources.getMessage(Locale.ENGLISH, "key.lang")); + assertEquals("key.lang US", "LANG en", resources.getMessage(Locale.US, "key.lang")); + assertEquals("key.lang ITALY", "LANG en", resources.getMessage(Locale.ITALY, "key.lang")); // cached en_US + assertEquals("key.lang German", "LANG de", resources.getMessage(Locale.GERMAN, "key.lang")); + assertEquals("key.lang GERMANY", "LANG de", resources.getMessage(Locale.GERMANY, "key.lang")); + + // Test country (& default) only keys + assertEquals("key.country FRANCE", "COUNTRY en_US", resources.getMessage(Locale.FRANCE, "key.country")); + assertEquals("key.country English", "COUNTRY en_US", resources.getMessage(Locale.ENGLISH, "key.country")); + assertEquals("key.country US", "COUNTRY en_US", resources.getMessage(Locale.US, "key.country")); + assertEquals("key.country ITALY", "COUNTRY en_US", resources.getMessage(Locale.ITALY, "key.country")); + assertEquals("key.country German", "COUNTRY en_US", resources.getMessage(Locale.GERMAN, "key.country")); + assertEquals("key.country GERMANY", "COUNTRY de_DE", resources.getMessage(Locale.GERMANY, "key.country")); + + // Test Unique Keys with wrong Locale + assertEquals("Wrong Locale en only", null, resources.getMessage(Locale.GERMAN, "key.en")); + assertEquals("Wrong Locale en_US only", "en_US only", resources.getMessage(Locale.GERMANY, "key.en_US")); + + // Run tests with common expected results + commonTests(resources); + } + + /** + * Test JSTL compatible PropertyMessageResources behaviour + */ + public void testJstlMode() { + + Locale.setDefault(Locale.US); + + // Create message resources - default Struts Behaviour + MessageResources resources = createMessageResources(FOO_RESOURCES, true, "JSTL"); + + // Test language (& default) only keys + assertEquals("key.lang FRANCE", "LANG default", resources.getMessage(Locale.FRANCE, "key.lang")); + assertEquals("key.lang English", "LANG en", resources.getMessage(Locale.ENGLISH, "key.lang")); + assertEquals("key.lang US", "LANG en", resources.getMessage(Locale.US, "key.lang")); + assertEquals("key.lang ITALY", "LANG default", resources.getMessage(Locale.ITALY, "key.lang")); + assertEquals("key.lang German", "LANG de", resources.getMessage(Locale.GERMAN, "key.lang")); + assertEquals("key.lang GERMANY", "LANG de", resources.getMessage(Locale.GERMANY, "key.lang")); + + // Test country (& default) only keys + assertEquals("key.country FRANCE", "COUNTRY default", resources.getMessage(Locale.FRANCE, "key.country")); + assertEquals("key.country English", "COUNTRY default", resources.getMessage(Locale.ENGLISH, "key.country")); + assertEquals("key.country US", "COUNTRY en_US", resources.getMessage(Locale.US, "key.country")); + assertEquals("key.country ITALY", "COUNTRY default", resources.getMessage(Locale.ITALY, "key.country")); + assertEquals("key.country German", "COUNTRY default", resources.getMessage(Locale.GERMAN, "key.country")); + assertEquals("key.country GERMANY", "COUNTRY de_DE", resources.getMessage(Locale.GERMANY, "key.country")); + + // Test Unique Keys with wrong Locale + assertEquals("Wrong Locale en only", null, resources.getMessage(Locale.GERMAN, "key.en")); + assertEquals("Wrong Locale en_US only", null, resources.getMessage(Locale.GERMANY, "key.en_US")); + + // Run tests with common expected results + commonTests(resources); + + } + + /** + * Test "PropertyResourceBundle" compatible PropertyMessageResources behaviour + */ + public void testResourceBundleMode() { + + Locale.setDefault(Locale.US); + + // Create message resources - default Struts Behaviour + MessageResources resources = createMessageResources(FOO_RESOURCES, true, "RESOURCE"); + + // Test language (& default) only keys + assertEquals("key.lang FRANCE", "LANG en", resources.getMessage(Locale.FRANCE, "key.lang")); + assertEquals("key.lang English", "LANG en", resources.getMessage(Locale.ENGLISH, "key.lang")); + assertEquals("key.lang US", "LANG en", resources.getMessage(Locale.US, "key.lang")); + assertEquals("key.lang ITALY", "LANG en", resources.getMessage(Locale.ITALY, "key.lang")); + assertEquals("key.lang German", "LANG de", resources.getMessage(Locale.GERMAN, "key.lang")); + assertEquals("key.lang GERMANY", "LANG de", resources.getMessage(Locale.GERMANY, "key.lang")); + + // Test country (& default) only keys + assertEquals("key.country FRANCE", "COUNTRY en_US", resources.getMessage(Locale.FRANCE, "key.country")); + assertEquals("key.country English", "COUNTRY en_US", resources.getMessage(Locale.ENGLISH, "key.country")); + assertEquals("key.country US", "COUNTRY en_US", resources.getMessage(Locale.US, "key.country")); + assertEquals("key.country ITALY", "COUNTRY en_US", resources.getMessage(Locale.ITALY, "key.country")); + assertEquals("key.country German", "COUNTRY en_US", resources.getMessage(Locale.GERMAN, "key.country")); + assertEquals("key.country GERMANY", "COUNTRY de_DE", resources.getMessage(Locale.GERMANY, "key.country")); + + // Test Unique Keys with wrong Locale + assertEquals("Wrong Locale en only", "en only", resources.getMessage(Locale.GERMAN, "key.en")); + assertEquals("Wrong Locale en_US only", "en_US only", resources.getMessage(Locale.GERMANY, "key.en_US")); + + // Run tests with common expected results + commonTests(resources); + } + + /** + * Tests with common expected results + */ + public void commonTests(MessageResources resources) { + + // Test "null" Locale + assertEquals("null Locale", "ALL default", resources.getMessage((Locale)null, "key.all")); + + // Test Default only key with all Locales + assertEquals("Check default en", "default only", resources.getMessage(Locale.ENGLISH, "key.default")); + assertEquals("Check default en_US", "default only", resources.getMessage(Locale.US, "key.default")); + assertEquals("Check default de", "default only", resources.getMessage(Locale.GERMAN, "key.default")); + assertEquals("Check default de_DE", "default only", resources.getMessage(Locale.GERMANY, "key.default")); + + // Test key in all locales + assertEquals("Check ALL en", "ALL en", resources.getMessage(Locale.ENGLISH, "key.all")); + assertEquals("Check ALL en_US", "ALL en_US", resources.getMessage(Locale.US, "key.all")); + assertEquals("Check ALL de", "ALL de", resources.getMessage(Locale.GERMAN, "key.all")); + assertEquals("Check ALL de_DE", "ALL de_DE", resources.getMessage(Locale.GERMANY, "key.all")); + + // Test key unique to each locale + assertEquals("Check en only", "en only", resources.getMessage(Locale.ENGLISH, "key.en")); + assertEquals("Check en_US only", "en_US only", resources.getMessage(Locale.US, "key.en_US")); + assertEquals("Check de only", "de only", resources.getMessage(Locale.GERMAN, "key.de")); + assertEquals("Check de_DE only", "de_DE only", resources.getMessage(Locale.GERMANY, "key.de_DE")); + + // Test unique keys with incorrect Locale + assertEquals("Missing default", null, resources.getMessage(Locale.ENGLISH, "missing")); + assertEquals("Missing de only", null, resources.getMessage(Locale.US, "key.de")); + assertEquals("Missing de_DE only", null, resources.getMessage(Locale.US, "key.de_DE")); + } + + /** + * Create the PropertyMessageResources. + */ + private MessageResources createMessageResources(String file, boolean returnNull, String mode) { + MessageResourcesConfig config = new MessageResourcesConfig(); + config.setNull(returnNull); + if (mode != null) { + config.setProperty("mode", mode); + } + PropertyMessageResourcesFactory factory = new PropertyMessageResourcesFactory(); + factory.setConfig(config); + factory.setReturnNull(returnNull); + return factory.createResources(file); + } +} Propchange: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/TestPropertyMessageResources.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/struts1/trunk/core/src/test/java/org/apache/struts/util/TestPropertyMessageResources.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Modified: struts/struts1/trunk/src/site/xdoc/userGuide/configuration.xml URL: http://svn.apache.org/viewvc/struts/struts1/trunk/src/site/xdoc/userGuide/configuration.xml?view=diff&rev=480549&r1=480548&r2=480549 ============================================================================== --- struts/struts1/trunk/src/site/xdoc/userGuide/configuration.xml (original) +++ struts/struts1/trunk/src/site/xdoc/userGuide/configuration.xml Wed Nov 29 04:16:15 2006 @@ -446,6 +446,34 @@ '. </p> + <p> + </p> + + <p> + The default <code>PropertyMessageResources</code> + implementation can operate in one of three modes: + </p> + + <ul> + <li><b>Default</b> - default, backwardly compatible, + Struts behaviour (i.e. the way its always worked).</li> + <li><b>JSTL</b> - compatible with how JSTL finds messages.</li> + <li><b>Resource</b> - compatible with how Java's + <code>PropertyResourceBundle</code> finds messages.</li> + </ul> + + <p> + The <i>mode</i> can be configured in the struts-config.xml + (for more details see <code>PropertyMessageResources</code> + <a href="../struts-core/apidocs/org/apache/struts/util/PropertyMessageResources.html"> + JavaDoc</a>):. + </p> + <source><![CDATA[ +<message-resources parameter="MyWebAppResources"> + <set-property key="mode" value="JSTL"/> +</message-resources> +]]></source> + </subsection> <a name="plugin_config"/>