This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch feature/WW-5481-text
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 026c992c083e21382121780760d48fd8ab20a6d5
Author: Lukasz Lenart <lukaszlen...@apache.org>
AuthorDate: Sat Nov 2 23:53:02 2024 +0100

    WW-5481 Extracts duplicated code
---
 .../text/AbstractLocalizedTextProvider.java        | 131 +++++-------
 .../struts2/text/GlobalLocalizedTextProvider.java  | 170 ++-------------
 .../apache/struts2/text/LocalizedTextProvider.java | 164 ++++++++++++++-
 .../struts2/text/StrutsLocalizedTextProvider.java  | 234 +++------------------
 4 files changed, 253 insertions(+), 446 deletions(-)

diff --git 
a/core/src/main/java/org/apache/struts2/text/AbstractLocalizedTextProvider.java 
b/core/src/main/java/org/apache/struts2/text/AbstractLocalizedTextProvider.java
index e7683cd6c..6185552c1 100644
--- 
a/core/src/main/java/org/apache/struts2/text/AbstractLocalizedTextProvider.java
+++ 
b/core/src/main/java/org/apache/struts2/text/AbstractLocalizedTextProvider.java
@@ -67,24 +67,18 @@ abstract class AbstractLocalizedTextProvider implements 
LocalizedTextProvider {
     private final Set<String> missingBundles = ConcurrentHashMap.newKeySet();
     private final ConcurrentMap<Integer, ClassLoader> delegatedClassLoaderMap 
= new ConcurrentHashMap<>();
 
-    /**
-     * Adds the bundle to the internal list of default bundles.
-     * If the bundle already exists in the list it will be re-added.
-     *
-     * @param resourceBundleName the name of the bundle to add.
-     */
     @Override
-    public void addDefaultResourceBundle(String resourceBundleName) {
+    public void addDefaultResourceBundle(String bundleName) {
         //make sure this doesn't get added more than once
         final ClassLoader ccl = getCurrentThreadContextClassLoader();
         synchronized (XWORK_MESSAGES_BUNDLE) {
             List<String> bundles = 
classLoaderMap.computeIfAbsent(ccl.hashCode(), k -> new 
CopyOnWriteArrayList<>());
-            bundles.remove(resourceBundleName);
-            bundles.add(0, resourceBundleName);
+            bundles.remove(bundleName);
+            bundles.add(0, bundleName);
         }
 
         if (LOG.isDebugEnabled()) {
-            LOG.debug("Added default resource bundle '{}' to default resource 
bundles for the following classloader '{}'", resourceBundleName, 
ccl.toString());
+            LOG.debug("Added default resource bundle '{}' to default resource 
bundles for the following classloader '{}'", bundleName, ccl.toString());
         }
     }
 
@@ -113,16 +107,8 @@ abstract class AbstractLocalizedTextProvider implements 
LocalizedTextProvider {
         }
     }
 
-    /**
-     * Returns a localized message for the specified key, aTextName.  Neither 
the key nor the
-     * message is evaluated.
-     *
-     * @param aTextName the message key
-     * @param locale    the locale the message should be for
-     * @return a localized message based on the specified key, or null if no 
localized message can be found for it
-     */
     @Override
-    public String findDefaultText(String aTextName, Locale locale) {
+    public String findDefaultText(String textKey, Locale locale) {
         List<String> localList = getCurrentBundleNames();
 
         for (String bundleName : localList) {
@@ -130,7 +116,7 @@ abstract class AbstractLocalizedTextProvider implements 
LocalizedTextProvider {
             if (bundle != null) {
                 reloadBundles();
                 try {
-                    return bundle.getString(aTextName);
+                    return bundle.getString(textKey);
                 } catch (MissingResourceException e) {
                     // will be logged when not found in any bundle
                 }
@@ -138,26 +124,17 @@ abstract class AbstractLocalizedTextProvider implements 
LocalizedTextProvider {
         }
 
         if (devMode) {
-            LOG.warn("Missing key [{}] in bundles [{}]!", aTextName, 
localList);
+            LOG.warn("Missing key [{}] in bundles [{}]!", textKey, localList);
         } else {
-            LOG.debug("Missing key [{}] in bundles [{}]!", aTextName, 
localList);
+            LOG.debug("Missing key [{}] in bundles [{}]!", textKey, localList);
         }
 
         return null;
     }
 
-    /**
-     * Returns a localized message for the specified key, aTextName, 
substituting variables from the
-     * array of params into the message.  Neither the key nor the message is 
evaluated.
-     *
-     * @param aTextName the message key
-     * @param locale    the locale the message should be for
-     * @param params    an array of objects to be substituted into the message 
text
-     * @return A formatted message based on the specified key, or null if no 
localized message can be found for it
-     */
     @Override
-    public String findDefaultText(String aTextName, Locale locale, Object[] 
params) {
-        String defaultText = findDefaultText(aTextName, locale);
+    public String findDefaultText(String textKey, Locale locale, Object[] 
params) {
+        String defaultText = findDefaultText(textKey, locale);
         if (defaultText != null) {
             MessageFormat mf = buildMessageFormat(defaultText, locale);
             return formatWithNullDetection(mf, params);
@@ -165,51 +142,27 @@ abstract class AbstractLocalizedTextProvider implements 
LocalizedTextProvider {
         return null;
     }
 
-
-    /**
-     * <p>
-     * Finds a localized text message for the given key, aTextName, in the 
specified resource
-     * bundle.
-     * </p>
-     *
-     * <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>
-     *
-     * <p>
-     * If a message is <b>not</b> found a WARN log will be logged.
-     * </p>
-     *
-     * @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.
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     */
     @Override
-    public String findText(ResourceBundle bundle, String aTextName, Locale 
locale, String defaultMessage, Object[] args,
+    public String findText(ResourceBundle bundle, String textKey, Locale 
locale, String defaultMessage, Object[] args,
                            ValueStack valueStack) {
         try {
             reloadBundles(valueStack.getContext());
 
-            String message = 
TextParseUtil.translateVariables(bundle.getString(aTextName), valueStack);
+            String message = 
TextParseUtil.translateVariables(bundle.getString(textKey), valueStack);
             MessageFormat mf = buildMessageFormat(message, locale);
 
             return formatWithNullDetection(mf, args);
         } catch (MissingResourceException ex) {
             if (devMode) {
-                LOG.warn("Missing key [{}] in bundle [{}]!", aTextName, 
bundle);
+                LOG.warn("Missing key [{}] in bundle [{}]!", textKey, bundle);
             } else {
-                LOG.debug("Missing key [{}] in bundle [{}]!", aTextName, 
bundle);
+                LOG.debug("Missing key [{}] in bundle [{}]!", textKey, bundle);
             }
         }
 
-        GetDefaultMessageReturnArg result = getDefaultMessage(aTextName, 
locale, valueStack, args, defaultMessage);
+        GetDefaultMessageReturnArg result = getDefaultMessage(textKey, locale, 
valueStack, args, defaultMessage);
         if (unableToFindTextForKey(result)) {
-            LOG.warn("Unable to find text for key '{}' in ResourceBundles for 
locale '{}'", aTextName, locale);
+            LOG.warn("Unable to find text for key '{}' in ResourceBundles for 
locale '{}'", textKey, locale);
         }
         return result != null ? result.message : null;
     }
@@ -425,20 +378,10 @@ abstract class AbstractLocalizedTextProvider implements 
LocalizedTextProvider {
         this.searchDefaultBundlesFirst = 
Boolean.parseBoolean(searchDefaultBundlesFirst);
     }
 
-    /**
-     * Finds the given resource bundle by it's name.
-     * <p>
-     * Will use <code>Thread.currentThread().getContextClassLoader()</code> as 
the classloader.
-     * </p>
-     *
-     * @param aBundleName the name of the bundle (usually it's FQN classname).
-     * @param locale      the locale.
-     * @return the bundle, <tt>null</tt> if not found.
-     */
     @Override
-    public ResourceBundle findResourceBundle(String aBundleName, Locale 
locale) {
+    public ResourceBundle findResourceBundle(String bundleName, Locale locale) 
{
         ClassLoader classLoader = getCurrentThreadContextClassLoader();
-        String key = createMissesKey(String.valueOf(classLoader.hashCode()), 
aBundleName, locale);
+        String key = createMissesKey(String.valueOf(classLoader.hashCode()), 
bundleName, locale);
 
         if (missingBundles.contains(key)) {
             return null;
@@ -449,7 +392,7 @@ abstract class AbstractLocalizedTextProvider implements 
LocalizedTextProvider {
             if (bundlesMap.containsKey(key)) {
                 bundle = bundlesMap.get(key);
             } else {
-                bundle = ResourceBundle.getBundle(aBundleName, locale, 
classLoader);
+                bundle = ResourceBundle.getBundle(bundleName, locale, 
classLoader);
                 bundlesMap.putIfAbsent(key, bundle);
             }
         } catch (MissingResourceException ex) {
@@ -458,15 +401,15 @@ abstract class AbstractLocalizedTextProvider implements 
LocalizedTextProvider {
                     if (bundlesMap.containsKey(key)) {
                         bundle = bundlesMap.get(key);
                     } else {
-                        bundle = ResourceBundle.getBundle(aBundleName, locale, 
delegatedClassLoaderMap.get(classLoader.hashCode()));
+                        bundle = ResourceBundle.getBundle(bundleName, locale, 
delegatedClassLoaderMap.get(classLoader.hashCode()));
                         bundlesMap.putIfAbsent(key, bundle);
                     }
                 } catch (MissingResourceException e) {
-                    LOG.debug("Missing resource bundle [{}]!", aBundleName, e);
+                    LOG.debug("Missing resource bundle [{}]!", bundleName, e);
                     missingBundles.add(key);
                 }
             } else {
-                LOG.debug("Missing resource bundle [{}]!", aBundleName);
+                LOG.debug("Missing resource bundle [{}]!", bundleName);
                 missingBundles.add(key);
             }
         }
@@ -656,6 +599,36 @@ abstract class AbstractLocalizedTextProvider implements 
LocalizedTextProvider {
         return null;
     }
 
+    protected String extractIndexedName(String textKey) {
+        String indexedTextName = null;
+        // calculate indexedTextName (collection[*]) if applicable
+        if (textKey.contains("[")) {
+            int i = -1;
+
+            indexedTextName = textKey;
+
+            while ((i = indexedTextName.indexOf('[', i + 1)) != -1) {
+                int j = indexedTextName.indexOf(']', i);
+                String a = indexedTextName.substring(0, i);
+                String b = indexedTextName.substring(j);
+                indexedTextName = a + "[*" + b;
+            }
+        }
+        return indexedTextName;
+    }
+
+    protected void logMissingText(Class<?> startClazz, String textKey, Locale 
locale, GetDefaultMessageReturnArg result, String indexedTextName) {
+        // could we find the text, if not log a WARN
+        if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
+            String warn = "Unable to find text for key '" + textKey + "' ";
+            if (indexedTextName != null) {
+                warn += " or indexed key '" + indexedTextName + "' ";
+            }
+            warn += "in class '" + startClazz.getName() + "' and locale '" + 
locale + "'";
+            LOG.debug(warn);
+        }
+    }
+
     static class MessageFormatKey {
         String pattern;
         Locale locale;
diff --git 
a/core/src/main/java/org/apache/struts2/text/GlobalLocalizedTextProvider.java 
b/core/src/main/java/org/apache/struts2/text/GlobalLocalizedTextProvider.java
index 33ab693dd..31ea21650 100644
--- 
a/core/src/main/java/org/apache/struts2/text/GlobalLocalizedTextProvider.java
+++ 
b/core/src/main/java/org/apache/struts2/text/GlobalLocalizedTextProvider.java
@@ -41,182 +41,42 @@ public class GlobalLocalizedTextProvider extends 
AbstractLocalizedTextProvider {
         addDefaultResourceBundle(STRUTS_MESSAGES_BUNDLE);
     }
 
-    /**
-     * Calls {@link #findText(Class aClass, String aTextName, Locale locale, 
String defaultMessage, Object[] args)}
-     * with aTextName as the default message.
-     *
-     * @param aClass    class name
-     * @param aTextName text name
-     * @param locale    the locale
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     * @see #findText(Class aClass, String aTextName, Locale locale, String 
defaultMessage, Object[] args)
-     */
     @Override
-    public String findText(Class<?> aClass, String aTextName, Locale locale) {
-        return findText(aClass, aTextName, locale, aTextName, new Object[0]);
+    public String findText(Class<?> startClazz, String textKey, Locale locale) 
{
+        return findText(startClazz, textKey, locale, textKey, new Object[0]);
     }
 
-    /**
-     * <p>
-     * 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 the message in the default resource bundles.</li>
-     * <li>If not found, 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>
-     *
-     * <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>
-     *
-     * @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 args           arguments
-     *                       resource bundle
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     */
     @Override
-    public String findText(Class<?> aClass, String aTextName, Locale locale, 
String defaultMessage, Object[] args) {
+    public String findText(Class<?> startClazz, String textKey, Locale locale, 
String defaultMessage, Object[] args) {
         ValueStack valueStack = ActionContext.getContext().getValueStack();
-        return findText(aClass, aTextName, locale, defaultMessage, args, 
valueStack);
-
+        return findText(startClazz, textKey, locale, defaultMessage, args, 
valueStack);
     }
 
-    /**
-     * <p>
-     * 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 the message in the default resource bundles.</li>
-     * <li>If not found, 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>
-     *
-     * <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>
-     *
-     * <p>
-     * If a message is <b>not</b> found a DEBUG level log warning will be 
logged.
-     * </p>
-     *
-     * @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 args           arguments
-     * @param valueStack     the value stack to use to evaluate expressions 
instead of the
-     *                       one in the ActionContext ThreadLocal
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     */
     @Override
-    public String findText(Class<?> aClass, String aTextName, Locale locale, 
String defaultMessage, Object[] args, ValueStack valueStack) {
-        String indexedTextName = null;
-        if (aTextName == null) {
-            LOG.warn("Trying to find text with null key!");
-            aTextName = "";
-        }
-        // calculate indexedTextName (collection[*]) if applicable
-        if (aTextName.contains("[")) {
-            int i = -1;
-
-            indexedTextName = aTextName;
-
-            while ((i = indexedTextName.indexOf('[', i + 1)) != -1) {
-                int j = indexedTextName.indexOf(']', i);
-                String a = indexedTextName.substring(0, i);
-                String b = indexedTextName.substring(j);
-                indexedTextName = a + "[*" + b;
-            }
+    public String findText(Class<?> startClazz, String textKey, Locale locale, 
String defaultMessage, Object[] args, ValueStack valueStack) {
+        if (textKey == null) {
+            LOG.debug("Key is null, short-circuit to default message");
+            return defaultMessage;
         }
+        String indexedTextName = extractIndexedName(textKey);
 
         // get default
-        GetDefaultMessageReturnArg result = 
getDefaultMessageWithAlternateKey(aTextName, indexedTextName, locale, 
valueStack, args, defaultMessage);
+        GetDefaultMessageReturnArg result = 
getDefaultMessageWithAlternateKey(textKey, indexedTextName, locale, valueStack, 
args, defaultMessage);
 
-        // could we find the text, if not log a warn
-        if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
-            String warn = "Unable to find text for key '" + aTextName + "' ";
-            if (indexedTextName != null) {
-                warn += " or indexed key '" + indexedTextName + "' ";
-            }
-            warn += "in class '" + aClass.getName() + "' and locale '" + 
locale + "'";
-            LOG.debug(warn);
-        }
+        logMissingText(startClazz, textKey, locale, result, indexedTextName);
 
         return result != null ? result.message : null;
     }
 
-    /**
-     * <p>
-     * Finds a localized text message for the given key, aTextName, in the 
specified resource bundle
-     * with aTextName as the default message.
-     * </p>
-     *
-     * <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>
-     *
-     * @param bundle    a resource bundle name
-     * @param aTextName text name
-     * @param locale    the locale
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     * @see #findText(ResourceBundle, String, Locale, String, Object[])
-     */
     @Override
-    public String findText(ResourceBundle bundle, String aTextName, Locale 
locale) {
-        return findText(bundle, aTextName, locale, aTextName, new Object[0]);
+    public String findText(ResourceBundle bundle, String textKey, Locale 
locale) {
+        return findText(bundle, textKey, locale, textKey, new Object[0]);
     }
 
-    /**
-     * <p>
-     * Finds a localized text message for the given key, aTextName, in the 
specified resource
-     * bundle.
-     * </p>
-     *
-     * <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>
-     *
-     * <p>
-     * If a message is <b>not</b> found a WARN log will be logged.
-     * </p>
-     *
-     * @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.
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     */
     @Override
-    public String findText(ResourceBundle bundle, String aTextName, Locale 
locale, String defaultMessage, Object[] args) {
+    public String findText(ResourceBundle bundle, String textKey, Locale 
locale, String defaultMessage, Object[] args) {
         ValueStack valueStack = ActionContext.getContext().getValueStack();
-        return findText(bundle, aTextName, locale, defaultMessage, args, 
valueStack);
+        return findText(bundle, textKey, locale, defaultMessage, args, 
valueStack);
     }
 
 }
diff --git 
a/core/src/main/java/org/apache/struts2/text/LocalizedTextProvider.java 
b/core/src/main/java/org/apache/struts2/text/LocalizedTextProvider.java
index fcaf39975..472ab35f4 100644
--- a/core/src/main/java/org/apache/struts2/text/LocalizedTextProvider.java
+++ b/core/src/main/java/org/apache/struts2/text/LocalizedTextProvider.java
@@ -26,24 +26,168 @@ import java.util.ResourceBundle;
 
 public interface LocalizedTextProvider extends Serializable {
 
-    String findDefaultText(String aTextName, Locale locale);
+    /**
+     * Returns a localized message for the specified key, aTextName.  Neither 
the key nor the
+     * message is evaluated.
+     *
+     * @param textKey the message key
+     * @param locale  the locale the message should be for
+     * @return a localized message based on the specified key, or null if no 
localized message can be found for it
+     */
+    String findDefaultText(String textKey, Locale locale);
 
-    String findDefaultText(String aTextName, Locale locale, Object[] params);
+    /**
+     * Returns a localized message for the specified key, aTextName, 
substituting variables from the
+     * array of params into the message.  Neither the key nor the message is 
evaluated.
+     *
+     * @param textKey the message key
+     * @param locale  the locale the message should be for
+     * @param params  an array of objects to be substituted into the message 
text
+     * @return A formatted message based on the specified key, or null if no 
localized message can be found for it
+     */
+    String findDefaultText(String textKey, Locale locale, Object[] params);
 
-    ResourceBundle findResourceBundle(String aBundleName, Locale locale);
+    /**
+     * Finds the given resource bundle by it's name.
+     * <p>
+     * Will use <code>Thread.currentThread().getContextClassLoader()</code> as 
the classloader.
+     * </p>
+     *
+     * @param bundleName the name of the bundle (usually it's FQN classname).
+     * @param locale     the locale.
+     * @return the bundle, <tt>null</tt> if not found.
+     */
+    ResourceBundle findResourceBundle(String bundleName, Locale locale);
 
-    String findText(Class<?> aClass, String aTextName, Locale locale);
+    /**
+     * Calls {@link #findText(Class startClazz, String textKey, Locale locale, 
String defaultMessage, Object[] args)}
+     * with textKey as the default message.
+     *
+     * @param startClazz class name
+     * @param textKey    text name
+     * @param locale     the locale
+     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
+     * @see #findText(Class startClazz, String textKey, Locale locale, String 
defaultMessage, Object[] args)
+     */
+    String findText(Class<?> startClazz, String textKey, Locale locale);
 
-    String findText(Class<?> aClass, String aTextName, Locale locale, String 
defaultMessage, Object[] args);
+    /**
+     * Finds a localized text message for the given key, textKey. Both the key 
and the message
+     * itself is evaluated as required.  The following algorithm is used to 
find the requested
+     * message:
+     *
+     * <ol>
+     * <li>Look for the message in the default resource bundles.</li>
+     * <li>If not found, 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.
+     *
+     * @param startClazz     the class whose name to use as the start point 
for the search
+     * @param textKey        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 args           arguments
+     *                       resource bundle
+     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
+     */
+    String findText(Class<?> startClazz, String textKey, Locale locale, String 
defaultMessage, Object[] args);
 
-    String findText(Class<?> aClass, String aTextName, Locale locale, String 
defaultMessage, Object[] args, ValueStack valueStack);
+    /**
+     * Finds a localized text message for the given key, textKey. Both the key 
and the message
+     * itself is evaluated as required.  The following algorithm is used to 
find the requested
+     * message:
+     *
+     * <ol>
+     * <li>Look for the message in the default resource bundles.</li>
+     * <li>If not found, 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 DEBUG level log warning will be 
logged.
+     *
+     * @param startClazz     the class whose name to use as the start point 
for the search
+     * @param textKey        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 args           arguments
+     * @param valueStack     the value stack to use to evaluate expressions 
instead of the
+     *                       one in the ActionContext ThreadLocal
+     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
+     */
+    String findText(Class<?> startClazz, String textKey, Locale locale, String 
defaultMessage, Object[] args, ValueStack valueStack);
 
-    String findText(ResourceBundle bundle, String aTextName, Locale locale);
+    /**
+     * Finds a localized text message for the given key, aTextName, in the 
specified resource bundle
+     * with aTextName as the default message.
+     * <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.
+     *
+     * @param bundle  a resource bundle name
+     * @param textKey text name
+     * @param locale  the locale
+     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
+     * @see #findText(ResourceBundle, String, Locale, String, Object[])
+     */
+    String findText(ResourceBundle bundle, String textKey, Locale locale);
 
-    String findText(ResourceBundle bundle, String aTextName, Locale locale, 
String defaultMessage, Object[] args);
+    /**
+     * 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 textKey        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.
+     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
+     */
+    String findText(ResourceBundle bundle, String textKey, Locale locale, 
String defaultMessage, Object[] args);
 
-    String findText(ResourceBundle bundle, String aTextName, Locale locale, 
String defaultMessage, Object[] args, ValueStack valueStack);
+    /**
+     * 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 textKey        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.
+     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
+     */
+    String findText(ResourceBundle bundle, String textKey, Locale locale, 
String defaultMessage, Object[] args, ValueStack valueStack);
 
-    void addDefaultResourceBundle(String resourceBundleName);
+    /**
+     * Adds the bundle to the internal list of default bundles.
+     * If the bundle already exists in the list it will be re-added.
+     *
+     * @param bundleName the name of the bundle to add.
+     */
+    void addDefaultResourceBundle(String bundleName);
 
 }
diff --git 
a/core/src/main/java/org/apache/struts2/text/StrutsLocalizedTextProvider.java 
b/core/src/main/java/org/apache/struts2/text/StrutsLocalizedTextProvider.java
index 3199d8a0f..bfdfe22fb 100644
--- 
a/core/src/main/java/org/apache/struts2/text/StrutsLocalizedTextProvider.java
+++ 
b/core/src/main/java/org/apache/struts2/text/StrutsLocalizedTextProvider.java
@@ -18,6 +18,8 @@
  */
 package org.apache.struts2.text;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import org.apache.struts2.ActionContext;
 import org.apache.struts2.ActionInvocation;
 import org.apache.struts2.ModelDriven;
@@ -25,8 +27,6 @@ import org.apache.struts2.conversion.impl.XWorkConverter;
 import org.apache.struts2.inject.Inject;
 import org.apache.struts2.util.ValueStack;
 import org.apache.struts2.util.reflection.ReflectionProvider;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 
 import java.beans.PropertyDescriptor;
 import java.util.Locale;
@@ -46,150 +46,26 @@ public class StrutsLocalizedTextProvider extends 
AbstractLocalizedTextProvider {
         addDefaultResourceBundle(STRUTS_MESSAGES_BUNDLE);
     }
 
-    /**
-     * Calls {@link #findText(Class aClass, String aTextName, Locale locale, 
String defaultMessage, Object[] args)}
-     * with aTextName as the default message.
-     *
-     * @param aClass    class name
-     * @param aTextName text name
-     * @param locale    the locale
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     * @see #findText(Class aClass, String aTextName, Locale locale, String 
defaultMessage, Object[] args)
-     */
     @Override
-    public String findText(Class<?> aClass, String aTextName, Locale locale) {
-        return findText(aClass, aTextName, locale, aTextName, new Object[0]);
+    public String findText(Class<?> startClazz, String textKey, Locale locale) 
{
+        return findText(startClazz, textKey, locale, textKey, new Object[0]);
     }
 
-    /**
-     * <p>
-     * 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>If {@link #searchDefaultBundlesFirst} is <code>true</code>, look 
for the message in the default resource bundles first.</li>
-     * <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 from the 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
-     * (Note: the lookup is not repeated again if {@link 
#searchDefaultBundlesFirst} was <code>true</code>).</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>
-     *
-     * <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>
-     *
-     * @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 args           arguments
-     *                       resource bundle
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     */
     @Override
-    public String findText(Class<?> aClass, String aTextName, Locale locale, 
String defaultMessage, Object[] args) {
+    public String findText(Class<?> startClazz, String textKey, Locale locale, 
String defaultMessage, Object[] args) {
         ValueStack valueStack = ActionContext.getContext().getValueStack();
-        return findText(aClass, aTextName, locale, defaultMessage, args, 
valueStack);
+        return findText(startClazz, textKey, locale, defaultMessage, args, 
valueStack);
 
     }
 
-    /**
-     * <p>
-     * 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>If {@link #searchDefaultBundlesFirst} is <code>true</code>, look 
for the message in the default resource bundles first.</li>
-     * <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 from the 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
-     * (Note: the lookup is not repeated again if {@link 
#searchDefaultBundlesFirst} was <code>true</code>).</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>
-     *
-     * <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>
-     *
-     * <p>
-     * If a message is <b>not</b> found a DEBUG level log warning will be 
logged.
-     * </p>
-     *
-     * @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 args           arguments
-     * @param valueStack     the value stack to use to evaluate expressions 
instead of the
-     *                       one in the ActionContext ThreadLocal
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     */
     @Override
-    public String findText(Class<?> aClass, String aTextName, Locale locale, 
String defaultMessage, Object[] args,
+    public String findText(Class<?> startClazz, String textKey, Locale locale, 
String defaultMessage, Object[] args,
                            ValueStack valueStack) {
-        String indexedTextName = null;
-        if (aTextName == null) {
-            LOG.warn("Trying to find text with null key!");
-            aTextName = "";
-        }
-        // calculate indexedTextName (collection[*]) if applicable
-        if (aTextName.contains("[")) {
-            int i = -1;
-
-            indexedTextName = aTextName;
-
-            while ((i = indexedTextName.indexOf('[', i + 1)) != -1) {
-                int j = indexedTextName.indexOf(']', i);
-                String a = indexedTextName.substring(0, i);
-                String b = indexedTextName.substring(j);
-                indexedTextName = a + "[*" + b;
-            }
+        if (textKey == null) {
+            LOG.debug("Key is null, short-circuit to default message");
+            return defaultMessage;
         }
+        String indexedTextName = extractIndexedName(textKey);
 
         // Allow for and track an early lookup for the message in the default 
resource bundles first, before searching the class hierarchy.
         // The early lookup is only performed when the text provider has been 
configured to do so, otherwise follow the standard processing order.
@@ -198,21 +74,21 @@ public class StrutsLocalizedTextProvider extends 
AbstractLocalizedTextProvider {
 
         // If search default bundles first is set true, call alternative logic 
first.
         if (searchDefaultBundlesFirst) {
-            result = getDefaultMessageWithAlternateKey(aTextName, 
indexedTextName, locale, valueStack, args, defaultMessage);
+            result = getDefaultMessageWithAlternateKey(textKey, 
indexedTextName, locale, valueStack, args, defaultMessage);
             performedInitialDefaultBundlesMessageLookup = true;
             if (!unableToFindTextForKey(result)) {
-                return result.message;  // Found a message in the default 
resource bundles for aTextName or indexedTextName.
+                return result.message;  // Found a message in the default 
resource bundles for textKey or indexedTextName.
             }
         }
 
         // search up class hierarchy
-        String msg = findMessage(aClass, aTextName, indexedTextName, locale, 
args, null, valueStack);
+        String msg = findMessage(startClazz, textKey, indexedTextName, locale, 
args, null, valueStack);
 
         if (msg != null) {
             return msg;
         }
 
-        if (ModelDriven.class.isAssignableFrom(aClass)) {
+        if (ModelDriven.class.isAssignableFrom(startClazz)) {
             ActionContext context = ActionContext.getContext();
             // search up model's class hierarchy
             ActionInvocation actionInvocation = context.getActionInvocation();
@@ -223,7 +99,7 @@ public class StrutsLocalizedTextProvider extends 
AbstractLocalizedTextProvider {
                 if (action instanceof ModelDriven) {
                     Object model = ((ModelDriven<?>) action).getModel();
                     if (model != null) {
-                        msg = findMessage(model.getClass(), aTextName, 
indexedTextName, locale, args, null, valueStack);
+                        msg = findMessage(model.getClass(), textKey, 
indexedTextName, locale, args, null, valueStack);
                         if (msg != null) {
                             return msg;
                         }
@@ -233,7 +109,7 @@ public class StrutsLocalizedTextProvider extends 
AbstractLocalizedTextProvider {
         }
 
         // nothing still? alright, search the package hierarchy now
-        for (Class<?> clazz = aClass;
+        for (Class<?> clazz = startClazz;
              (clazz != null) && !clazz.equals(Object.class);
              clazz = clazz.getSuperclass()) {
 
@@ -241,7 +117,7 @@ public class StrutsLocalizedTextProvider extends 
AbstractLocalizedTextProvider {
             while (basePackageName.lastIndexOf('.') != -1) {
                 basePackageName = basePackageName.substring(0, 
basePackageName.lastIndexOf('.'));
                 String packageName = basePackageName + ".package";
-                msg = getMessage(packageName, locale, aTextName, valueStack, 
args);
+                msg = getMessage(packageName, locale, textKey, valueStack, 
args);
 
                 if (msg != null) {
                     return msg;
@@ -258,22 +134,22 @@ public class StrutsLocalizedTextProvider extends 
AbstractLocalizedTextProvider {
         }
 
         // see if it's a child property
-        int idx = aTextName.indexOf('.');
+        int idx = textKey.indexOf('.');
 
         if (idx != -1) {
             String newKey = null;
             String prop = null;
 
-            if 
(aTextName.startsWith(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX)) {
-                idx = aTextName.indexOf('.', 
XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length());
+            if 
(textKey.startsWith(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX)) {
+                idx = textKey.indexOf('.', 
XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length());
 
                 if (idx != -1) {
-                    prop = 
aTextName.substring(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length(), 
idx);
-                    newKey = XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX + 
aTextName.substring(idx + 1);
+                    prop = 
textKey.substring(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length(), 
idx);
+                    newKey = XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX + 
textKey.substring(idx + 1);
                 }
             } else {
-                prop = aTextName.substring(0, idx);
-                newKey = aTextName.substring(idx + 1);
+                prop = textKey.substring(0, idx);
+                newKey = textKey.substring(idx + 1);
             }
 
             if (prop != null) {
@@ -310,74 +186,28 @@ public class StrutsLocalizedTextProvider extends 
AbstractLocalizedTextProvider {
         // Note: The default bundles lookup may already have been performed 
(via alternate early lookup),
         //       so we check first to avoid repeating the same operation twice.
         if (!performedInitialDefaultBundlesMessageLookup) {
-            result = getDefaultMessageWithAlternateKey(aTextName, 
indexedTextName, locale, valueStack, args, defaultMessage);
+            result = getDefaultMessageWithAlternateKey(textKey, 
indexedTextName, locale, valueStack, args, defaultMessage);
         }
 
-        // could we find the text, if not log a warn
-        if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
-            String warn = "Unable to find text for key '" + aTextName + "' ";
-            if (indexedTextName != null) {
-                warn += " or indexed key '" + indexedTextName + "' ";
-            }
-            warn += "in class '" + aClass.getName() + "' and locale '" + 
locale + "'";
-            LOG.debug(warn);
-        }
+        logMissingText(startClazz, textKey, locale, result, indexedTextName);
 
         return result != null ? result.message : null;
     }
 
-    /**
-     * <p>
-     * Finds a localized text message for the given key, aTextName, in the 
specified resource bundle
-     * with aTextName as the default message.
-     * </p>
-     *
-     * <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>
-     *
-     * @param bundle    a resource bundle name
-     * @param aTextName text name
-     * @param locale    the locale
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     * @see #findText(java.util.ResourceBundle, String, java.util.Locale, 
String, Object[])
-     */
     @Override
-    public String findText(ResourceBundle bundle, String aTextName, Locale 
locale) {
-        return findText(bundle, aTextName, locale, aTextName, new Object[0]);
+    public String findText(ResourceBundle bundle, String textKey, Locale 
locale) {
+        return findText(bundle, textKey, locale, textKey, new Object[0]);
     }
 
-    /**
-     * <p>
-     * Finds a localized text message for the given key, aTextName, in the 
specified resource
-     * bundle.
-     * </p>
-     *
-     * <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>
-     *
-     * <p>
-     * If a message is <b>not</b> found a WARN log will be logged.
-     * </p>
-     *
-     * @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.
-     * @return the localized text, or null if none can be found and no 
defaultMessage is provided
-     */
     @Override
-    public String findText(ResourceBundle bundle, String aTextName, Locale 
locale, String defaultMessage, Object[] args) {
+    public String findText(ResourceBundle bundle, String textKey, Locale 
locale, String defaultMessage, Object[] args) {
         ValueStack valueStack = ActionContext.getContext().getValueStack();
-        return findText(bundle, aTextName, locale, defaultMessage, args, 
valueStack);
+        return findText(bundle, textKey, locale, defaultMessage, args, 
valueStack);
     }
 
     @Inject
     public void setReflectionProvider(ReflectionProvider reflectionProvider) {
         this.reflectionProvider = reflectionProvider;
     }
+
 }

Reply via email to