You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2024/11/02 22:53:18 UTC
(struts) 02/02: WW-5481 Extracts duplicated code
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 <lu...@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;
}
+
}