You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by ni...@apache.org on 2006/11/29 13:16:16 UTC

svn commit: r480549 - in /struts/struts1/trunk: apps/examples/src/main/webapp/WEB-INF/validator/ core/ core/src/main/java/org/apache/struts/util/ core/src/test/java/org/apache/struts/util/ src/site/xdoc/userGuide/

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>
+ *      &lt;message-resources parameter="mypackage.MyMessageResources"&gt;
+ *          &lt;set-property key="mode" value="JSTL"/&gt;
+ *      &lt;/message-resources&gt;
+ * </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>
+ *      &lt;message-resources parameter="mypackage.MyMessageResources"&gt;
+ *          &lt;set-property key="mode" value="resource"/&gt;
+ *      &lt;/message-resources&gt;
+ * </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"/>