Commits

Anonymous committed 58d5e14

WW-1390
- Adding hasKey to the TextProvider interface

git-svn-id: http://svn.opensymphony.com/svn/xwork/branches/xwork_1-2@1280e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (8)

src/java/com/opensymphony/xwork/ActionSupport.java

         return ActionContext.getContext().getLocale();
     }
 
+    public boolean hasKey(String key) {
+    	return textProvider.hasKey(key);
+    }
+    
     public String getText(String aTextName) {
         return textProvider.getText(aTextName);
     }

src/java/com/opensymphony/xwork/DefaultTextProvider.java

  */
 public class DefaultTextProvider implements TextProvider, Serializable, Unchainable {
 
-    private static final Object[] EMPTY_ARGS = new Object[0];
+	private static final long serialVersionUID = 5559215734038422388L;
+
+	private static final Object[] EMPTY_ARGS = new Object[0];
 
     public static final DefaultTextProvider INSTANCE = new DefaultTextProvider();
 
     private DefaultTextProvider() {
     }
+    
+    public boolean hasKey(String key) {
+    	return getText(key) == null ? false : true;
+    }
 
     public String getText(String key) {
         return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale());

src/java/com/opensymphony/xwork/TextProvider.java

  */
 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.
      *

src/java/com/opensymphony/xwork/TextProviderSupport.java

         this.localeProvider = provider;
     }
 
+    
+    /**
+     * 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;
+    	OgnlValueStack stack = ActionContext.getContext().getValueStack();
+    	if (clazz != null) {
+            message =  LocalizedTextUtil.findText(clazz, key, getLocale(), null, new Object[0], stack, false);
+        } else {
+            message = LocalizedTextUtil.findText(bundle, key, getLocale(), null, new Object[0], stack, false);
+        }
+    	return message == null ? false : true;
+    }
+    
 
     /**
      * Get a text from the resource bundles associated with this action.

src/java/com/opensymphony/xwork/util/LocalizedTextUtil.java

         return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
 
     }
-
+    
     /**
+     * <b>This method call will log a warning message (in debug level) if message is not found</b>
+     * 
      * Finds a localized text message for the given key, aTextName. Both the key and the message
      * itself is evaluated as required.  The following algorithm is used to find the requested
      * message:
      * @return the localized text, or null if none can be found and no defaultMessage is provided
      */
     public static String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args, OgnlValueStack valueStack) {
+        return findText(aClass, aTextName, locale, defaultMessage, args, valueStack, true);
+    }
+
+    /**
+     * Finds a localized text message for the given key, aTextName. Both the key and the message
+     * itself is evaluated as required.  The following algorithm is used to find the requested
+     * message:
+     * <p/>
+     * <ol>
+     * <li>Look for message in aClass' class hierarchy.
+     * <ol>
+     * <li>Look for the message in a resource bundle for aClass</li>
+     * <li>If not found, look for the message in a resource bundle for any implemented interface</li>
+     * <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
+     * </ol></li>
+     * <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
+     * the model's class hierarchy (repeat sub-steps listed above).</li>
+     * <li>If not found, look for message in child property.  This is determined by evaluating
+     * the message key as an OGNL expression.  For example, if the key is
+     * <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
+     * object.  If so, repeat the entire process fromthe beginning with the object's class as
+     * aClass and "address.state" as the message key.</li>
+     * <li>If not found, look for the message in aClass' package hierarchy.</li>
+     * <li>If still not found, look for the message in the default resource bundles.</li>
+     * <li>Return defaultMessage</li>
+     * </ol>
+     * <p/>
+     * When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
+     * message for that specific key cannot be found, the general form will also be looked up
+     * (i.e. user.phone[*]).
+     * <p/>
+     * If a message is found, it will also be interpolated.  Anything within <code>${...}</code>
+     * will be treated as an OGNL expression and evaluated as such.
+     * <p/>
+     * If a message is <b>not</b> found a WARN log will be logged.
+     *
+     * @param aClass         the class whose name to use as the start point for the search
+     * @param aTextName      the key to find the text message for
+     * @param locale         the locale the message should be for
+     * @param defaultMessage the message to be returned if no text message can be found in any
+     *                       resource bundle
+     * @param valueStack     the value stack to use to evaluate expressions instead of the
+     *                       one in the ActionContext ThreadLocal
+     * @param warnIfNoMessageFound log warning message (in debug level) if message is not found.
+     * @return the localized text, or null if none can be found and no defaultMessage is provided
+     */
+    public static String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args, OgnlValueStack valueStack, boolean warnIfNoMessageFound) {
         String indexedTextName = null;
         if (aTextName == null) {
             LOG.warn("Trying to find text with null key!");
         }
         
         // could we find the text, if not log a warn
-        if (unableToFindTextForKey(result)) {
+        if (warnIfNoMessageFound && unableToFindTextForKey(result)) {
         	String warn = "Unable to find text for key '" + aTextName + "' ";
         	if (indexedTextName != null) {
         		warn += " or indexed key '" + indexedTextName + "' ";
         OgnlValueStack valueStack = ActionContext.getContext().getValueStack();
         return findText(bundle, aTextName, locale, defaultMessage, args, valueStack);
     }
-
+    
+    
     /**
+     * <b>This method will log a warning (in debug level) if no message is found.</b>
+     * 
      * Finds a localized text message for the given key, aTextName, in the specified resource
      * bundle.
      * <p/>
      * @param valueStack the OGNL value stack.
      */
     public static String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args, OgnlValueStack valueStack) {
+    	 return findText(bundle, aTextName, locale, defaultMessage, args, valueStack, true) ;
+    }
+
+    /**
+     * Finds a localized text message for the given key, aTextName, in the specified resource
+     * bundle.
+     * <p/>
+     * If a message is found, it will also be interpolated.  Anything within <code>${...}</code>
+     * will be treated as an OGNL expression and evaluated as such.
+     * <p/>
+     * If a message is <b>not</b> found a WARN log will be logged.
+     * 
+     * @param bundle     the bundle
+     * @param aTextName  the key
+     * @param locale     the locale
+     * @param defaultMessage  the default message to use if no message was found in the bundle
+     * @param args       arguments for the message formatter.
+     * @param valueStack the OGNL value stack.
+     */
+    public static String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args, OgnlValueStack valueStack, boolean warnIfNoMessageFound) {
         try {
             reloadBundles();
 
         }
 
         GetDefaultMessageReturnArg result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
-        if (unableToFindTextForKey(result)) {
+        if (warnIfNoMessageFound && unableToFindTextForKey(result)) {
             LOG.warn("Unable to find text for key '" + aTextName + "' in ResourceBundles for locale '" + locale + "'");
         }
-        return result.message;
+        return result == null ? null : result.message;
     }
 
     /**

src/java/com/opensymphony/xwork/validator/DelegatingValidatorContext.java

         return localeProvider.getLocale();
     }
 
+    public boolean hasKey(String key) {
+    	return textProvider.hasKey(key);
+    }
+    
     public String getText(String aTextName) {
         return textProvider.getText(aTextName);
     }

src/test/com/opensymphony/xwork/TextProviderSupportTest.java

  * Unit test for {@link TextProviderSupport}.
  *
  * @author Claus Ibsen
+ * @author tmjee
  */
 public class TextProviderSupportTest extends XWorkTestCase {
 
     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"));

src/test/com/opensymphony/xwork/util/LocalizedTextUtilTest.java

  */
 public class LocalizedTextUtilTest extends TestCase {
 
+	public void testHasKey() throws Exception {
+		OgnlValueStack stack = new OgnlValueStack();
+		assertNull(LocalizedTextUtil.findText(MyObject.class, "doesnotexist.nothing", Locale.ENGLISH, null, new Object[0], stack, false));
+	}
+	
+	
 	public void testNpeWhenClassIsPrimitive() throws Exception {
 		OgnlValueStack stack = new OgnlValueStack();
 		stack.push(new MyObject());