1. opensymphony
  2. xwork

Commits

rainerh  committed 9de864c

XW-501 corrected logic as pointed out by Vlad at http://forums.opensymphony.com/thread.jspa?threadID=73335&messageID=163571
o added hasKey() method to TextProvider interface
o ported fix from xwork 1.2 branch

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@1813e221344d-f017-0410-9bd5-d282ab1896d7

  • Participants
  • Parent commits 69cef0c
  • Branches master

Comments (0)

Files changed (11)

File src/java/com/opensymphony/xwork2/ActionSupport.java

View file
         }
     }
 
+    public boolean hasKey(String key) {
+        return textProvider.hasKey(key);
+    }
+
     public String getText(String aTextName) {
         return textProvider.getText(aTextName);
     }
     public String input() throws Exception {
         return INPUT;
     }
-    
+
     public String doDefault() throws Exception {
         return SUCCESS;
     }
      * See also {@link com.opensymphony.xwork2.Action#execute()}.
      *
      * @return returns {@link #SUCCESS}
-     * @throws Exception  can be thrown by subclasses.
+     * @throws Exception can be thrown by subclasses.
      */
     public String execute() throws Exception {
         return SUCCESS;
      * Stops the action invocation immediately (by throwing a PauseException) and causes the action invocation to return
      * the specified result, such as {@link #SUCCESS}, {@link #INPUT}, etc.
      * <p/>
-     *
+     * <p/>
      * The next time this action is invoked (and using the same continuation ID), the method will resume immediately
      * after where this method was called, with the entire call stack in the execute method restored.
      * <p/>
-     *
+     * <p/>
      * Note: this method can <b>only</b> be called within the {@link #execute()} method.
      * <!-- END SNIPPET: pause-method -->
      *

File src/java/com/opensymphony/xwork2/DefaultTextProvider.java

View file
     public DefaultTextProvider() {
     }
 
+    public boolean hasKey(String key) {
+        return getText(key) != null;
+    }
+
     public String getText(String key) {
         return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale());
     }

File src/java/com/opensymphony/xwork2/TextProvider.java

View file
 public interface TextProvider {
 
     /**
+     * Checks if a message key exists.
+     *
+     * @param key
+     * @return boolean true if key exists, false otherwise.
+     */
+    boolean hasKey(String key);
+
+    /**
      * Gets a message based on a message key, or null if no message is found.
      *
      * @param key the resource bundle key that is to be searched for

File src/java/com/opensymphony/xwork2/TextProviderSupport.java

View file
         this.localeProvider = localeProvider;
     }
 
+
+    /**
+     * Checks if a key is available in the resource bundles associated with this action.
+     * The resource bundles are searched, starting with the one associated
+     * with this particular action, and testing all its superclasses' bundles.
+     * It will stop once a bundle is found that contains the given text. This gives
+     * a cascading style that allow global texts to be defined for an application base
+     * class.
+     */
+    public boolean hasKey(String key) {
+    	String message = null;
+    	if (clazz != null) {
+            message =  LocalizedTextUtil.findText(clazz, key, getLocale(), null, new Object[0] );
+        } else {
+            message = LocalizedTextUtil.findText(bundle, key, getLocale(), null, new Object[0]);
+        }
+    	return message != null;
+    }
+
     /**
      * Get a text from the resource bundles associated with this action.
      * The resource bundles are searched, starting with the one associated

File src/java/com/opensymphony/xwork2/util/LocalizedTextUtil.java

View file
         if (unableToFindTextForKey(result)) {
             LOG.warn("Unable to find text for key '" + aTextName + "' in ResourceBundles for locale '" + locale + "'");
         }
-        return result.message;
+        return result != null ? result.message : null;
     }
 
     /**

File src/java/com/opensymphony/xwork2/validator/CompositeTextProvider.java

View file
+package com.opensymphony.xwork2.validator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.*;
+
+import com.opensymphony.xwork2.TextProvider;
+import com.opensymphony.xwork2.util.ValueStack;
+
+
+/**
+ * This is a composite {@link TextProvider} that takes in an array or {@link java.util.List} of {@link TextProvider}s, it will
+ * consult each of them in order to get a composite result. To know how each method behaves, please refer to the
+ * javadoc for each methods.
+ *
+ * @author tmjee
+ * @version $Date$ $Id$
+ */
+public class CompositeTextProvider implements TextProvider {
+
+    private static final Log LOG = LogFactory.getLog(CompositeTextProvider.class);
+
+    private List textProviders = new ArrayList();
+
+    /**
+     * Instantiates a {@link CompositeTextProvider} with some predefined <code>textProviders</code>.
+     * @param textProviders
+     */
+    public CompositeTextProvider(List textProviders) {
+        for (Iterator i = textProviders.iterator(); i.hasNext(); ) {
+            Object obj = i.next();
+            if (obj instanceof TextProvider) {
+                this.textProviders.add(obj);
+            }
+            else {
+                LOG.warn("object ["+obj+"] provided in list ["+textProviders+"] is not a TextProvider instance, ignoring it");
+            }
+        }
+    }
+
+    /**
+     * Instantiates a {@link CompositeTextProvider} with some predefined <code>textProviders</code>.
+     * @param textProviders
+     */
+    public CompositeTextProvider(TextProvider[] textProviders) {
+        this(Arrays.asList(textProviders));
+    }
+
+    /**
+     * @see {@link com.opensymphony.xwork2.TextProvider#hasKey(String)}
+     * It will consult each individual {@link TextProvider}s and return true if either one of the
+     * {@link TextProvider} has such a <code>key></code> else false.
+     *
+     * @param key
+     * @return
+     */
+    public boolean hasKey(String key) {
+        // if there's a key in either text providers we are ok, else try the next text provider
+        for (Iterator i = textProviders.iterator(); i.hasNext(); ) {
+            TextProvider _textProvider = (TextProvider) i.next();
+            if (_textProvider.hasKey(key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first valid message for this
+     * <code>key</code>
+     * @see {@link com.opensymphony.xwork.TextProvider#getText(String)}
+     * @param key
+     * @return
+     */
+    public String getText(String key) {
+        return getText(key, key, Collections.EMPTY_LIST);
+    }
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first valid message for this
+     * <code>key</code> before returning <code>defaultValue</code> if every else fails.
+     * @see {@link com.opensymphony.xwork.TextProvider#getText(String, String)}
+     * @param key
+     * @param defaultValue
+     * @return
+     */
+    public String getText(String key, String defaultValue) {
+        return getText(key, defaultValue, Collections.EMPTY_LIST);
+    }
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first valid message for this
+     * <code>key</code>, before returining <code>defaultValue</code>
+     * if every else fails.
+     * @see {@link com.opensymphony.xwork.TextProvider#getText(String, String, String)}
+     * @param key
+     * @param defaultValue
+     * @param obj
+     * @return
+     */
+    public String getText(String key, String defaultValue, final String obj) {
+        return getText(key, defaultValue, new ArrayList() {
+            {
+                add(obj);
+            }
+        });
+    }
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first valid message for this
+     * <code>key</code>.
+     * @see {@link com.opensymphony.xwork.TextProvider#getText(String, java.util.List)}
+     * @param key
+     * @param args
+     * @return
+     */
+    public String getText(String key, List args) {
+        return getText(key, key, args);
+    }
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first valid message for this
+     * <code>key</code>.
+     * @see {@link com.opensymphony.xwork.TextProvider#getText(String, String[])}
+     * @param key
+     * @param args
+     * @return
+     */
+    public String getText(String key, String[] args) {
+        return getText(key, key, args);
+    }
+
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first valid message for this
+     * <code>key</code>, before returining <code>defaultValue</code>
+     * @see {@link com.opensymphony.xwork.TextProvider#getText#getText(String, String, java.util.List)}
+     * @param key
+     * @param defaultValue
+     * @param args
+     * @return
+     */
+    public String getText(String key, String defaultValue, List args) {
+        // if there's one text provider that gives us a msg not the same as defaultValue
+        // for this key, we are ok, else try the next
+        // text provider
+        for (Iterator i = textProviders.iterator(); i.hasNext(); ) {
+            TextProvider _textProvider = (TextProvider) i.next();
+            String msg = _textProvider.getText(key, defaultValue, args);
+            if (msg != null && (!msg.equals(defaultValue))) {
+                return msg;
+            }
+        }
+        return defaultValue;
+    }
+
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first valid message for this
+     * <code>key</code>, before returining <code>defaultValue</code>.
+     * @see {@link com.opensymphony.xwork.TextProvider#getText(String, String, String[])}
+     * @param key
+     * @param defaultValue
+     * @param args
+     * @return
+     */
+    public String getText(String key, String defaultValue, String[] args) {
+        // if there's one text provider that gives us a msg not the same as defaultValue
+        // for this key, we are ok, else try the next
+        // text provider
+        for (Iterator i = textProviders.iterator(); i.hasNext(); ) {
+            TextProvider _textProvider = (TextProvider) i.next();
+            String msg = _textProvider.getText(key, defaultValue, args);
+            if (msg != null && (!msg.equals(defaultValue))) {
+                return msg;
+            }
+        }
+        return defaultValue;
+    }
+
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first valid message for this
+     * <code>key</code>, before returining <code>defaultValue</code>
+     *
+     * @see {@link com.opensymphony.xwork.TextProvider#getText(String, String, java.util.List, com.opensymphony.xwork.util.OgnlValueStack)}
+     * @param key
+     * @param defaultValue
+     * @param args
+     * @param stack
+     * @return
+     */
+    public String getText(String key, String defaultValue, List args, ValueStack stack) {
+        // if there's one text provider that gives us a msg not the same as defaultValue
+        // for this key, we are ok, else try the next
+        // text provider
+        for (Iterator i = textProviders.iterator(); i.hasNext(); ) {
+            TextProvider _textProvider = (TextProvider) i.next();
+            String msg = _textProvider.getText(key, defaultValue, args, stack);
+            if (msg != null && (!msg.equals(defaultValue))) {
+                return msg;
+            }
+        }
+        return defaultValue;
+    }
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first valid message for this
+     * <code>key</code>, before returining <code>defaultValue</code>
+     * @see {@link com.opensymphony.xwork.TextProvider#getText(String, String, String[], com.opensymphony.xwork2.util.ValueStack)}
+     * @param key
+     * @param defaultValue
+     * @param args
+     * @param stack
+     * @return
+     */
+    public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
+        // if there's one text provider that gives us a msg not the same as defaultValue
+        // for this key, we are ok, else try the next
+        // text provider
+        for (Iterator i = textProviders.iterator(); i.hasNext(); ) {
+            TextProvider _textProvider = (TextProvider) i.next();
+            String msg = _textProvider.getText(key, defaultValue, args, stack);
+            if (msg != null && (!msg.equals(defaultValue))) {
+                return msg;
+            }
+        }
+        return defaultValue;
+    }
+
+
+    /**
+     * It will consult each {@link TextProvider}s and return the first non-null {@link ResourceBundle}.
+     * @see {@link TextProvider#getTexts(String)}
+     * @param bundleName
+     * @return
+     */
+    public ResourceBundle getTexts(String bundleName) {
+        // if there's one text provider that gives us a non-null resource bunlde for this bundleName, we are ok, else try the next
+        // text provider
+        for (Iterator i = textProviders.iterator(); i.hasNext(); ) {
+            TextProvider _textProvider = (TextProvider) i.next();
+            ResourceBundle bundle  = _textProvider.getTexts(bundleName);
+            if (bundle != null) {
+                return bundle;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * It will consult each {@link com.opensymphony.xwork.TextProvider}s and return the first non-null {@link ResourceBundle}.
+     * @see {@link TextProvider#getTexts()}
+     * @return
+     */
+    public ResourceBundle getTexts() {
+        // if there's one text provider that gives us a non-null resource bundle, we are ok, else try the next
+        // text provider
+        for (Iterator i = textProviders.iterator(); i.hasNext(); ) {
+            TextProvider _textProvider = (TextProvider) i.next();
+            ResourceBundle bundle = _textProvider.getTexts();
+            if (bundle != null) {
+                return bundle;
+            }
+        }
+        return null;
+    }
+}
+

File src/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java

View file
 import java.util.Map;
 import java.util.ResourceBundle;
 
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.LocaleProvider;
-import com.opensymphony.xwork2.TextProvider;
-import com.opensymphony.xwork2.TextProviderFactory;
-import com.opensymphony.xwork2.ValidationAware;
+import com.opensymphony.xwork2.*;
 import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.util.logging.Logger;
 import com.opensymphony.xwork2.util.logging.LoggerFactory;
      * are used internally to set errors and get and set error text.
      */
     public DelegatingValidatorContext(ValidationAware validationAware, TextProvider textProvider,
-            LocaleProvider localeProvider)
-    {
+                                      LocaleProvider localeProvider) {
         this.textProvider = textProvider;
         this.validationAware = validationAware;
         this.localeProvider = localeProvider;
         return localeProvider.getLocale();
     }
 
+    public boolean hasKey(String key) {
+    	return textProvider.hasKey(key);
+    }
+    
     public String getText(String aTextName) {
         return textProvider.getText(aTextName);
     }
     }
 
     public String getText(String key, String defaultValue, List args, ValueStack stack) {
-        return textProvider.getText(key,defaultValue,args,stack);
+        return textProvider.getText(key, defaultValue, args, stack);
     }
 
     public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
-        return textProvider.getText(key,defaultValue,args,stack);
+        return textProvider.getText(key, defaultValue, args, stack);
     }
 
     public ResourceBundle getTexts() {
     }
 
     public static TextProvider makeTextProvider(Object object, LocaleProvider localeProvider) {
-        if (object instanceof TextProvider) {
-            return (TextProvider) object;
+        // the object argument passed through here will most probably be an ActionSupport decendant which does
+        // implements TextProvider.
+        if ((object != null) && (object instanceof TextProvider)) {
+            return new CompositeTextProvider(new TextProvider[]{
+                    ((TextProvider) object),
+                    new TextProviderSupport(object.getClass(), localeProvider)
+            });
         } else {
             return new TextProviderFactory().createInstance(object.getClass(), localeProvider);
         }
     }
 
     /**
-     * An implementation of ValidationAware which logs errors and messages. 
+     * An implementation of ValidationAware which logs errors and messages.
      */
     private static class LoggingValidationAware implements ValidationAware {
 

File src/test/com/opensymphony/xwork2/TextProviderSupportTest.java

View file
     private TextProviderSupport tp;
     private java.util.ResourceBundle rb;
 
+    public void testHasKey() throws Exception {
+    	assertTrue(tp.hasKey("hello"));
+    	assertFalse(tp.hasKey("not.in.bundle"));
+    }
+    
     public void testSimpleGetTexts() throws Exception {
         assertEquals("Hello World", tp.getText("hello"));
         assertEquals("not.in.bundle", tp.getText("not.in.bundle"));

File src/test/com/opensymphony/xwork2/validator/CompositeTextProviderTest.java

View file
+package com.opensymphony.xwork2.validator;
+
+import com.opensymphony.xwork2.TextProvider;
+import com.opensymphony.xwork2.TextProviderSupport;
+import com.opensymphony.xwork2.LocaleProvider;
+import com.opensymphony.xwork2.XWorkTestCase;
+
+import java.util.ArrayList;
+import java.util.ResourceBundle;
+import java.util.Locale;
+
+/**
+ * <code>CompositeTextProviderTest</code>
+ *
+ * @author <a href="mailto:hermanns@aixcept.de">Rainer Hermanns</a>
+ * @version $Id: $
+ */
+public class CompositeTextProviderTest extends XWorkTestCase {
+
+
+    private CompositeTextProvider textProvider = null;
+
+
+    public void testGetText() throws Exception {
+        // we should get the text from the 1st text provider
+        assertEquals(textProvider.getText("name"), "1 name");
+        assertEquals(textProvider.getText("age"), "1 age");
+        assertEquals(textProvider.getText("dog"), "This is a dog");
+        assertEquals(textProvider.getText("cat"), "This is a cat");
+        assertEquals(textProvider.getText("car"), "This is a car");
+        assertEquals(textProvider.getText("bike"), "This is a bike");
+        assertEquals(textProvider.getText("someNonExistingKey"), "someNonExistingKey");
+    }
+
+
+    public void testGetTextWithDefaultValues() throws Exception {
+        assertEquals(textProvider.getText("name", "some default name"), "1 name");
+        assertEquals(textProvider.getText("age", "some default age"), "1 age");
+        assertEquals(textProvider.getText("no_such_key", "default value"), "default value");
+        assertEquals(textProvider.getText("dog", "some default dog"), "This is a dog");
+        assertEquals(textProvider.getText("cat", "some default cat"), "This is a cat");
+        assertEquals(textProvider.getText("car", "some default car"), "This is a car");
+        assertEquals(textProvider.getText("bike", "some default bike"), "This is a bike");
+    }
+
+
+    public void testGetTextWithDefaultValuesAndArgs() throws Exception {
+        assertEquals(textProvider.getText("goodnight", "say good night", "Adam"), "1 good night Adam");
+        assertEquals(textProvider.getText("goodnight", "say good night", new String[] { "Adam" }), "1 good night Adam");
+        assertEquals(textProvider.getText("goodnight", "say good night", new ArrayList() { {add("Adam");} }), "1 good night Adam");
+        assertEquals(textProvider.getText("goodmorning", "say good morning", new String[] { "Jack", "Jim" }), "1 good morning Jack and Jim");
+        assertEquals(textProvider.getText("goodmorning", "say good morning", new ArrayList() { { add("Jack"); add("Jim"); }}), "1 good morning Jack and Jim");
+    }
+
+    public void testHasKey() throws Exception {
+        assertTrue(textProvider.hasKey("name"));
+        assertTrue(textProvider.hasKey("age"));
+        assertTrue(textProvider.hasKey("cat"));
+        assertTrue(textProvider.hasKey("dog"));
+        assertTrue(textProvider.hasKey("car"));
+        assertTrue(textProvider.hasKey("bike"));
+        assertTrue(textProvider.hasKey("goodnight"));
+        assertTrue(textProvider.hasKey("goodmorning"));
+        assertFalse(textProvider.hasKey("nosuchkey"));
+    }
+
+    public void testGetResourceBundleByName() throws Exception {
+        assertNotNull(textProvider.getTexts("com.opensymphony.xwork2.validator.CompositeTextProviderTestResourceBundle1"));
+        assertNotNull(textProvider.getTexts("com.opensymphony.xwork2.validator.CompositeTextProviderTestResourceBundle2"));
+        assertNull(textProvider.getTexts("com.opensymphony.xwork2.validator.CompositeTextProviderTestResourceBundle3"));
+    }
+
+    public void testGetResourceBundle() throws Exception {
+        assertNotNull(textProvider.getTexts());
+        // we should get the first resource bundle where 'car' and 'bike' has a i18n msg
+        assertNotNull(textProvider.getTexts().getString("car"));
+        assertNotNull(textProvider.getTexts().getString("bike"));
+    }
+
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        textProvider = new CompositeTextProvider(new TextProvider[] {
+                new TextProviderSupport(ResourceBundle.getBundle("com.opensymphony.xwork2.validator.CompositeTextProviderTestResourceBundle1"),
+                        new LocaleProvider() {
+                            public Locale getLocale() {
+                                return Locale.ENGLISH;
+                            }
+                        }),
+                new TextProviderSupport(ResourceBundle.getBundle("com.opensymphony.xwork2.validator.CompositeTextProviderTestResourceBundle2"),
+                        new LocaleProvider() {
+                            public Locale getLocale() {
+                                return Locale.ENGLISH;
+                            }
+                        })
+
+        });
+    }
+
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        textProvider = null;
+    }
+}

File src/test/com/opensymphony/xwork2/validator/CompositeTextProviderTestResourceBundle1.properties

View file
+# common in both CompositeTextProviderTestResourceBundle1.properties
+# and CompositeTextProviderTestResourceBunlde2.properties
+name=1 name
+age=1 age
+goodnight=1 good night {0}
+goodmorning=1 good morning {0} and {1}
+
+# specific to CompositeTextProviderTestResourceBundle1.properties
+car=This is a car
+bike=This is a bike

File src/test/com/opensymphony/xwork2/validator/CompositeTextProviderTestResourceBundle2.properties

View file
+# common in both CompositeTextProviderTestResourceBundle1.properties
+# and CompositeTextProviderTestResourceBunlde2.properties
+name=2 name
+age=2 age
+goodnight=2 good night {0}
+goodmorning=2 good morning {0} and {1}
+
+
+# specific to CompositeTextProviderTestResourceBundle2.properties
+cat=This is a cat
+dog=This is a dog